パターンと置換リストを使用してファイル名を繰り返し変更します。

パターンと置換リストを使用してファイル名を繰り返し変更します。

次のファイル構造があります。

  • いくつかのディレクトリ
    • 一部のファイル.txt
    • ここに別のファイルがあります。 .log
    • .mp3 ファイルもあります。
  • その他のディレクトリ
    • 他のfile.txtと一緒に
  • ルートレベル file.txt
  • ルートレベルの他のファイル.ext

今私が望むのは、パターン/代替ペアを含む別のファイルを入力として使用して、そのファイルに基づいてそのファイルの名前を繰り返し変更する小さなスクリプトを実行することです。これにより、すべての「another」(大文字と小文字を区別しない)が「foo」に置き換えられるか、すべての「some」が「bar」に置き換えられます。

私はファイルを繰り返してその入力ファイルを読み取るためにいくつかの方法を試しましたが、何も機能せず、最終的に誤ってテストスクリプトを上書きしました。しかしls、、、whilesed多くmv使われています。

自分で知らない2つは、ファイル名のスペースを処理する方法と、以前のパターンマッチングで名前が変更されたファイルを処理しない方法です。

たぶんあなたが私に正しい方向を伝えることができますか?

答え1

TOP="`pwd -P`" \
find . -type d -exec sh -c '
   for d
   do
      cd "$d" && \
         find . ! -name . -prune -type f -exec sh -c '\''
            while IFS=\; read -r pat repl
            do
               rename "s/$pat/$repl/g" "$@"
               N=$#
               for unmoved
               do
                  if [ -f "$unmoved" ]
                  then
                     set X ${1+"$@"} "$unmoved"
                     shift
                  fi
               done
               shift "$N"
               case $# in 0 ) break ;; esac
            done < patterns.csv
         '\'' x \{\} +
      cd "$TOP"
   done
' x {} +
  • findネットワークディレクトリを設定して飲み込むだけですsh。これにより、呼び出し回数が最小限に抑えられますsh
  • find各ディレクトリのネットワークファイルをregular奥行きレベル1に設定し、shおしゃべりをしてください。これにより、renameユーティリティの呼び出し回数が最小限に抑えられます。
  • while個々のペアで読み取り、pattern <-> replacementすべてのファイルに適用するようにループを設定しますregular
  • 進行中rename-イギリスrename処理後もファイルがまだ存在するかどうかを記録します。ファイルがまだ存在するということは、何らかの理由で名前を変更することはできず、次の反復で試行されることを意味しますpat/repl。 OTOH、ファイル名が正常に変更された場合は、pat/replコマンドライン引数リストからファイルを削除して、ファイルに次の反復を適用しません。

答え2

rPairs="/tmp/rename_pairs" \
find . -type f -exec sh -c '
   while read -r old new; do
      rename "s/$old/$new/i" "$@"
   done < "$rPairs"
' x {} +

名前の変更 ペア ファイルに ASCII 以外の文字がなく、ファイルが検索パスから離れているとします。

答え3

Rakesh Sharmaの回答に従って、より多くの努力をし、しばらく寝てから正しい方向を見つけました。

ついに私は次のスクリプトを思いついた。

#!/bin/bash


while IFS=";" read pattern replacement
do
  if [[ ! -z $pattern ]]
  then
    echo "Checking files for pattern '$pattern'."

    find ./files -name "*$pattern*" -type f | while read fpath
    do
      fname=$(basename "$fpath")
      dname=$(dirname "$fpath")

      echo "  Found file '$fname' in directory '$dname'. Renaming to '${fname/$pattern/$replacement}'."
      mv -- "$fpath" "$dname/${fname/$pattern/$replacement}"
    done
  fi
done < patterns.csv

ファイルを読み込み、塗りつぶしpattern.csvと変数行を繰り返します。 2番目のステップでは、現在のパターンと一致するディレクトリ内のすべてのファイルを探します。これは、2番目のパターンが一致したときにファイル名を変更し直すのを防ぐために必要です。これは失敗するためです。最後に、ファイルを含むディレクトリの名前を変更するためにシェルパラメータ置換を使用するのではなく、ファイル自体の名前のみを変更します。$pattern$replacement./files

うまくいかないのは、大文字と小文字を区別しない一致を置き換えることです。しかし、私はそれで生きることができます。

答え4

覚えておくべき重要なことは、ディレクトリツリーをナビゲートするプロセスが遅いので、一度だけ行うことです。私たちが最初にやりたいことは、findツリーのディレクトリを見てみることです。各ディレクトリについて、regular filesその下のすべてのディレクトリを探します(ここでは再帰はありません)。その後、成功したかどうかを記録しながら、これらのファイル名に名前変更変換を適用します。成功したら、whileループを終了して、次のpatt / replがファイルに適用されないようにします。

tempd="`mktemp -d`" \
find . -type d -exec sh -c '
   cd "$1" && \
   for f in ./*
   do
      [ -f "$f" ] || continue
      while IFS=\; read -r patt repl
      do
         case $f in
            ./*"$patt"* )
               rename -v "s/$patt/$repl/g" "$f" 2>&1 | tee "$tempd/$f"
               case $(< "$tempf/$f") in "$f renamed "* ) break ;; esac ;;
         esac
      done < /tmp/patterns.csv
   done
' {} {} \;

関連情報