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 を見ましたが、それは十分に異なっていたので、私の弱いスクリプト能力は動揺していました。
ありがとうございました。
あなたの質問には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 を参照してくださいこれを行うための小さな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
フラグは非常に重要です。最初に一致したファイル( -p
がmkdir
が既存のディレクトリを作成しようとして停止することを指摘するためにArronicalに感謝します )2>/dev/null
スクリプトは、それ自体と同じ名前のディレクトリを作成できないと文句を言います(それでも動作します)ので、エラーを捨てます-これは、ワンライナーとして実行する場合は必要ありませんif [[ -d "${file%%[.-]*}" ]]; then
その名前のディレクトリがある場合(mkdir
が成功した場合)...if [[ -f "$file" ]]
ファイル(ディレクトリなどではない)を扱っている場合...mv -v -- "$file" "${file%%[.-]*}"
一致するディレクトリに移動します。小さな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
このスクリプトでは、すべての(関連する)ファイルに拡張子があると想定しています。ファイルに拡張子がない場合、何も起こりません。それが問題である場合、言及してください、簡単に変更することができます。
その後、ファイルは対応するフォルダーに移動されます。