web-dev-qa-db-ja.com

フォルダー内のファイルの名前を親フォルダーの名前に変更できますか?

これはユニークな状況であり、例を使ってよりよく説明できます

例1 Aというフォルダーがあり、B.mkvC.srtがフォルダー内の2つのファイルであるとします。これらのファイルの名前をA.mkvA.srtに変更します。

実用的な例は、American Sniper (2014)と呼ばれるフォルダーとAmerican.Sniper.2014.1080p.BluRay.x264.YIFY.mkvAmerican.Sniper.2014.Subtitle.BluRay.ShaAnig.srtはフォルダー内の2つのファイルであり、これらのファイルの名前をAmerican Sniper (2014).mkvAmerican Sniper (2014).srtに変更したい場合です。

例2

これは再帰的である必要があります。たとえば、AB、およびCというフォルダーがAフォルダー内の2つのサブフォルダーであるとします。 BとCの内容は次のようなものです

Example

に変換されるはずです

Result

フォルダーAでアルゴリズム/スクリプト/コマンドを実行しても

3番目にすべきことは、隠しファイルを無視することです

Example

結果になるはずです

Result

最後に、一度に複数のフォルダで動作するはずです

2
Sumeet Deshmukh

この質問にアプローチする方法は複数あります。最良の結果を得るには、指示を注意深くお読みください。

この答えでは:

  1. Pythonのアプローチ
  2. find + bashアプローチ
  3. globstarを使用したbashのみのアプローチ

1. Pythonソリューション

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

2. findコマンドと-execフラグを使用した解決策

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、シェル名として設定されるため、無関係ですが、"{}"は重要です。これは、findbashに引数として渡すファイル名の引用されたプレースホルダーです。

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を使用すると、出力がないため、コマンドはサイレントです。


3.よりシンプルなアプローチ:Bash and glob star

上記の2つのアプローチは、再帰的なツリートラバーサルを使用します。この例では、ディレクトリツリーに2つのレベル(映画のディレクトリとファイル)しかないため、以前のシェルコマンドを次のように簡略化できます。

for f in */* ;do fp=$(dirname "$f"); ext="${f##*.}" ; echo "$f" "$fp"/"$fp"."$ext" ;done

繰り返しますが、同じアイデアです-echomvに置き換えてください。

テスト結果は同じです:

$ 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
3

与えられた

$ 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を削除してください。

3
steeldriver