これはユニークな状況であり、例を使ってよりよく説明できます
例1 A
というフォルダーがあり、B.mkv
とC.srt
がフォルダー内の2つのファイルであるとします。これらのファイルの名前をA.mkv
とA.srt
に変更します。
実用的な例は、American Sniper (2014)
と呼ばれるフォルダーとAmerican.Sniper.2014.1080p.BluRay.x264.YIFY.mkv
とAmerican.Sniper.2014.Subtitle.BluRay.ShaAnig.srt
はフォルダー内の2つのファイルであり、これらのファイルの名前をAmerican Sniper (2014).mkv
とAmerican Sniper (2014).srt
に変更したい場合です。
例2
これは再帰的である必要があります。たとえば、A
、B
、およびC
というフォルダーがA
フォルダー内の2つのサブフォルダーであるとします。 BとCの内容は次のようなものです
に変換されるはずです
フォルダーA
でアルゴリズム/スクリプト/コマンドを実行しても
3番目にすべきことは、隠しファイルを無視することです
例
結果になるはずです
最後に、一度に複数のフォルダで動作するはずです
この質問にアプローチする方法は複数あります。最良の結果を得るには、指示を注意深くお読みください。
この答えでは:
find
+ bash
アプローチPythonはシステム管理のための非常に強力な言語であり、os.walk()
関数を介してディレクトリツリーを走査できます。以下に示すスクリプトでは、まさにそれを行っています。すべてのファイルを見つけ、各ファイルを操作します。各ファイルへのフルパス、ファイルの拡張子を決定し、os.rename()
関数で名前を変更します。
#!/usr/bin/env python3
import os,sys
def get_all_files(treeroot):
for dir,subdirs,files in os.walk(treeroot):
for f in files:
if f in __file__: continue
fullpath = os.path.realpath( os.path.join(dir,f) )
extension = fullpath.split('.')[-1]
newpath = os.path.join(dir, dir + '.' + extension)
print('Move ' + fullpath + ' to ' + newpath )
# os.rename(fullpath,newpath)
def main():
top_dir="."
# If directory not given, assume cwd
if len(sys.argv) == 2: top_dir=sys.argv[1]
get_all_files(top_dir)
if __== '__main__' : main()
注:非常に重要なのは、# os.rename(fullpath,newpath)
の前に#
を削除する必要があるファイルの名前を実際に変更することです。
スクリプトのすべての標準ルールが適用されます。-最上位ディレクトリにadd_location_name.py
として保存します-chmod +x ./add_location_name.py
で実行可能ファイルを作成します-./add_location_name.py
で実行します
これが実際にどのように機能するかの例を次に示します。 Movie A (2016)
とMovie B (2016)
の2つのディレクトリを作成しました。それらの中には、2つのファイルがあります。スクリプトは同じディレクトリにあります。
$ tree
.
├── add_location_name.py
├── Movie A (2014)
│ ├── filea.mkv
│ └── fileb.srt
└── Movie B (2016)
├── filea.mkv
└── fileb.srt
したがって、スクリプトを実行すると、次の出力が表示されます。
$ ./add_location_name.py
Move /home/xieerqi/testdir/Movie A (2014)/fileb.srt to ./Movie A (2014)/./Movie A (2014).srt
Move /home/xieerqi/testdir/Movie A (2014)/filea.mkv to ./Movie A (2014)/./Movie A (2014).mkv
Move /home/xieerqi/testdir/Movie B (2016)/fileb.srt to ./Movie B (2016)/./Movie B (2016).srt
Move /home/xieerqi/testdir/Movie B (2016)/filea.mkv to ./Movie B (2016)/./Movie B (2016).mkv
find
コマンドは、特に複数レベルのディレクトリツリーで操作を実行する場合に、多くの点で役立ちます。この特定のケースでは、これを使用してすべてのファイルを除外し、ファイルの名前変更操作を実行できます。
まず、解決策を紹介しましょう。
find -type f -exec bash -c 'fp=$(dirname "$1");fn=$(basename "$fp");px="${1##*.}";mv "$1" "$fp"/"$fn"."$px"' sh "{}" \;
長くて怖いですね。しかし、心配しないで、それがどのように機能するかを調べましょう。
まず最初に、2つのことが行われていることを認識する必要があります。find
コマンドはすべてのファイルを検索し、bash
部分は実際に名前を変更する部分です。外側では、単純なコマンドが表示されます:
find -type f -exec <COMMAND> \;
これは、すべてのファイルのみを検索し(ディレクトリまたはシンボリックリンクは検索しません)、ファイルを見つけるたびに他のコマンドを呼び出します。この場合、持っている特定のコマンドはbash
です。
bash -c 'fp=$(dirname "$1");fn=$(basename "$fp");px="${1##*.}";mv "$1" "$fp"/"$fn"."$px"' sh "{}"
パートは何をするのでしょうか?まあ、まず最初に構造を認識します:bash -c 'command1;command2' arg0 arg1
。 -c
フラグが使用されるときはいつでも、最初のコマンドライン引数arg0
が$0
、シェル名として設定されるため、無関係ですが、"{}"
は重要です。これは、find
がbash
に引数として渡すファイル名の引用されたプレースホルダーです。
Bashコマンドの内部で、ファイルパスfp=$(dirname "$1")
、ディレクトリ名fn=$(basename "$fp")
、およびファイル拡張子またはプレフィックスpx="${1##*.}"
を抽出します。最上位のディレクトリからコマンドを実行しているため(これは非常に重要です!)、これはすべて非常にうまく機能します。最後に、mv "$1" "$fp"/"$fn"."$px"' sh "{}"
は、find
が提供した元のファイルの名前を、これらすべての変数を使用して"$fp"/"$fn"."$px"
で作成した新しいファイル名に変更します。
コマンドのテストは、以前と同じディレクトリで実行されます。
$ tree
.
├── Movie A (2014)
│ ├── filea.mkv
│ └── fileb.srt
└── Movie B (2016)
├── filea.mkv
└── fileb.srt
2 directories, 4 files
そして、echo
の代わりにmv
を使用してコマンドを実行すると、各ファイル名がそれぞれ名前変更されていることがわかります。
$ find -type f -exec bash -c 'fp=$(dirname "$1");fn=$(basename "$fp");px="${1##*.}";echo "$1" "$fp"/"$fn"."$px"' sh ">
./Movie A (2014)/fileb.srt ./Movie A (2014)/Movie A (2014).srt
./Movie A (2014)/filea.mkv ./Movie A (2014)/Movie A (2014).mkv
./Movie B (2016)/fileb.srt ./Movie B (2016)/Movie B (2016).srt
./Movie B (2016)/filea.mkv ./Movie B (2016)/Movie B (2016).mkv
覚えておいてください:上記のコマンドは、テストにのみecho
を使用します。 mv
を使用すると、出力がないため、コマンドはサイレントです。
上記の2つのアプローチは、再帰的なツリートラバーサルを使用します。この例では、ディレクトリツリーに2つのレベル(映画のディレクトリとファイル)しかないため、以前のシェルコマンドを次のように簡略化できます。
for f in */* ;do fp=$(dirname "$f"); ext="${f##*.}" ; echo "$f" "$fp"/"$fp"."$ext" ;done
繰り返しますが、同じアイデアです-echo
をmv
に置き換えてください。
テスト結果は同じです:
$ tree
.
├── add_location_name.py
├── Movie A (2014)
│ ├── filea.mkv
│ └── fileb.srt
└── Movie B (2016)
├── filea.mkv
└── fileb.srt
2 directories, 5 files
$ for f in */* ;do fp=$(dirname "$f"); ext="${f##*.}" ; echo "$f" "$fp"/"$fp"."$ext" ;done
Movie A (2014)/filea.mkv Movie A (2014)/Movie A (2014).mkv
Movie A (2014)/fileb.srt Movie A (2014)/Movie A (2014).srt
Movie B (2016)/filea.mkv Movie B (2016)/Movie B (2016).mkv
Movie B (2016)/fileb.srt Movie B (2016)/Movie B (2016).srt
与えられた
$ tree
.
└── A
├── B
│ ├── somefile
│ ├── T.txt
│ ├── X.srt
│ └── Z.mkv
└── C
├── somefile
├── T.txt
├── W.mkv
└── Y.srt
3 directories, 8 files
それから
$ find A -type f \( -name '*.mkv' -o -name '*.srt' \) -not -name '.*' -exec sh -c '
for f; do
dir="${f%/*}" ; ext="${f##*.}" ; new="${dir##*/}"
echo mv -- "$f" "${dir}/${new}.${ext}"
done' sh {} +
mv -- A/C/Y.srt A/C/C.srt
mv -- A/C/W.mkv A/C/C.mkv
mv -- A/B/Z.mkv A/B/B.mkv
mv -- A/B/X.srt A/B/B.srt
(一度、sureになったら、echo
を削除してください。