web-dev-qa-db-ja.com

名前付きファイルのように自己名前付きディレクトリに移動する

1つのディレクトリに数千のファイルがあり、次のようなディレクトリで照合したいと思います。

これから:

└── Files
    ├── AAA.mkv
    ├── AAA.nfo
    ├── AAA-picture.jpg
    ├── BBB.mp4
    ├── BBB.srt
    ├── BBB-clip.mp4
    ├── CCC.avi
    ├── CCC.srt
    ├── CCC-clip.mov
    └── CCC.nfo

これに:

└── Files
    ├── AAA
    │   ├── AAA.mkv
    │   ├── AAA.nfo
    │   └── AAA-picture.jpg
    ├── BBB
    │   ├── BBB.mp4
    │   ├── BBB.srt
    │   └── BBB-clip.mp4
    └── CCC
         ├── CCC.avi
         ├── CCC.srt
         ├── CCC-clip.mov
         └── CCC.nfo

ファイル名は長さや単語数が異なり、スペースで区切られたり、場合によってはハイフンで区切られたりする場合があります(「-short」で終わるものに加えて、主にさまざまな形式/コンテナのビデオファイル:mov/mpg/mkv/mp4/avi/ogg。字幕付きのものもあります。メタデータ(.nfoまたは-clip)が関連付けられたファイルもあります

編集:プライマリファイルはビデオです(これはディレクトリ名を描画したい場所です)。関連ファイルはメタデータを表します。拡張子のみによって命名が異なるものもあります。 -clip.mp4 -clip.movや-picture.jpgのように、ベースファイル名には6種類以上のバリエーションがあります。要約すると、AAA.mkvはAAAというディレクトリに移動します。次に、AAAで始まるすべてのメタデータファイルが結合されます(つまり、この例ではAAA-picture.jpgとAAA.nfo)。 AAA-picture.jpgファイルの場合、実際にはベース名はサブストリングです。単に区切り文字としてハイフンを使用することはおそらく比較的安全だと思いますが、 '-clip'または '-picture'全体がより安全になります。

手根管症候群を起こさずにこれを行うにはどうすればよいですか? this を見ましたが、それは十分に異なっていたので、私の弱いスクリプト能力は動揺していました。

ありがとうございました。

4
MrFinn

あなたの質問にはbashのタグが付けられていますが、このようなタスクにbashを使用するのは(私の謙虚な意見では)やや面倒です。 pythonを使用することをお勧めします。これには、複雑なタスクに適した多くの機能があり、この回答はその言語を使用したソリューションを提供するためです。

基本的に、ここで発生することは、正規表現を使用して複数の区切り文字でファイル名を分割し、最初の部分のみを取得し、それらの最初の部分の一意のセットを新しいディレクトリのベース名として使用することです。

次に、最上位ディレクトリを再度走査し、適切な場所でファイルを並べ替えます。

スクリプトは壮観なことは何も行いません。実際、アルゴリズム分析では、ネストされたforループのため、これはあまりうまくいきませんが、「迅速で汚れた、しかし実行可能な」ソリューションでは大丈夫です。各行の機能に興味がある場合は、機能を説明するために追加されたコメントがたくさんあります

、デモでは、テスト目的でのみ新しいファイル名の印刷のみを示しています。 os.rename()部分のコメントを外して、実際にファイルを移動します。

デモ

bash-4.3$ # Same directory structure as in OP example
bash-4.3$ ls TESTDIR
bash-4.3$ # now run script
AAA  AAA.mkv  AAA.nfo  AAA-picture.jpg  BBB  BBB-clip.mp4  BBB.mp4  BBB.srt
bash-4.3$ ./collate_files.py ./TESTDIR
/home/xieerqi/TESTDIR/AAA/AAA-picture.jpg
/home/xieerqi/TESTDIR/AAA/AAA.mkv
/home/xieerqi/TESTDIR/AAA/AAA.nfo
/home/xieerqi/TESTDIR/BBB/BBB.srt
/home/xieerqi/TESTDIR/BBB/BBB.mp4
/home/xieerqi/TESTDIR/BBB/BBB-clip.mp4

スクリプト自体

#!/usr/bin/env python
import re,sys,os

top_dir = os.path.realpath(sys.argv[1])

# Create list of items in directory first
# splitting names at multiple separators
dir_list = [os.path.join(top_dir,re.split("[.-]",f)[0])
            for f in os.listdir(top_dir)
]
# Creating set ensures we will have unique
# directory namings
dir_set = set(dir_list)

# Make these directories first
for dir in dir_set:
    if not os.path.exists(dir):
        os.mkdir(dir)

# now get all files only, no directories
files_list = [f for f in os.listdir(top_dir)
              if os.path.isfile(os.path.join(top_dir,f))
]

# Traverse lists of directories and files,
# check if a filename starts with directory
# that we're testing now, and if it does - move
# the file to that directory
for dir in dir_set:
    id_string = os.path.basename(dir)
    for f in files_list:
        filename = os.path.basename(f)
        if filename.startswith(id_string):
           new_path = os.path.join(dir,filename)
           print(new_path)
           #os.rename(f,new_path)

その他の注意事項:

  • スクリプトは、他の複数の区切り文字(re.split()関数内)でファイルを分割するように適合させることができます。角括弧内に追加("[.-]"を意味)したい文字を追加します。
  • 可動部分は、os.rename()関数で実行されます。または、import shutilおよびshutil.move()関数を使用できます。 https://stackoverflow.com/a/8858026/3701431 を参照してください
5

これを行うための小さなbashスクリプトを作成し、OP、@ dannysauer、@ Arronical、および@Scottからのコメントのおかげで簡略化および改善しました

#!/bin/bash
for file in *
  do mkdir -p "${file%%[.-]*}" 2>/dev/null
    if [[ -d "${file%%[.-]*}" ]]; then
       if [[ -f "$file" ]]; then
         echo mv -v -- "$file" "${file%%[.-]*}"
       fi
    fi
done

最初にechoで実行し、次にechoを削除して、実際にファイルを移動します。スクリプトは、ファイルを移動するディレクトリから実行する必要があります。必要に応じて、ここに1行のコマンドを示します。

for file in *; do mkdir -p "${file%%[.-]*}"; if [[ -d "${file%%[.-]*}" ]]; then if [[ -f "$file" ]]; then echo mv -v -- "$file" "${file%%[.-]*}"; fi ; fi ; done

(もう一度、テスト後にechoを削除します)

説明:

  • for file in *; do mkdir -p "${file%%[.-]*}"は、各ファイルの名前の最初の部分の名前(最初のハイフンまたはドット文字まで)でディレクトリを作成します。ここで-pフラグは非常に重要です。最初に一致したファイル( -pmkdirが既存のディレクトリを作成しようとして停止することを指摘するためにArronicalに感謝します
  • 2>/dev/nullスクリプトは、それ自体と同じ名前のディレクトリを作成できないと文句を言います(それでも動作します)ので、エラーを捨てます-これは、ワンライナーとして実行する場合は必要ありません
  • if [[ -d "${file%%[.-]*}" ]]; thenその名前のディレクトリがある場合(mkdirが成功した場合)...
  • if [[ -f "$file" ]]ファイル(ディレクトリなどではない)を扱っている場合...
  • mv -v -- "$file" "${file%%[.-]*}"一致するディレクトリに移動します。
9
Zanna

小さなpythonスクリプトの場合:

#!/usr/bin/env python3
import shutil
import os
import sys

dr = sys.argv[1]

for f in os.listdir(dr):
    split = f.rfind("."); short = f.find("-")
    if split != -1:
        extension = f[split:]
        newname = f[:short] if short != -1 else f[:split]
        target = os.path.join(dr, newname)
        if not os.path.exists(target):
            os.mkdir(target)
        shutil.move(os.path.join(dr, f), os.path.join(target, f))

使用するには:

  • 空のファイルにコピーします
  • move_into.pyとして保存します
  • ディレクトリを引数として実行します:

    python3 /path/to/move_into.py /path/to/directory
    

このスクリプトでは、すべての(関連する)ファイルに拡張子があると想定しています。ファイルに拡張子がない場合、何も起こりません。それが問題である場合、言及してください、簡単に変更することができます。

説明

  • スクリプトは、可能な拡張を探します。
  • 存在しない場合、スクリプトはファイル(またはディレクトリ)をそのままにします。
  • それ以外の場合、ファイルは「-」で分割されます(存在する場合)。その後、必要に応じて最初のセクションがフォルダーの作成に使用されます
  • そうでない場合は、ファイルのベース名を使用してフォルダーに名前を付けます。

その後、ファイルは対応するフォルダーに移動されます。

5
Jacob Vlijm