ファイル構造は次のとおりです。
- ディレクトリ
- いくつかのファイル.txt
- 別のファイル here.log
- さらに別のファイル.mp3
- 別のディレクトリ
- 他のファイル.txt
- ルート レベルのファイル.txt
- ルート レベルにある別のファイル.ext
私が今やりたいことは、何らかのパターン/置換ペアを含む別のファイルを入力として受け取り、それに従ってこれらのファイルを再帰的に名前変更する小さなスクリプトを実行することです。これにより、すべての「another」(大文字と小文字を区別しない) が「foo」に置き換えられ、すべての「some」が「bar」に置き換えられます。
すでに、ファイルを反復処理して入力ファイルを読み取るなど、さまざまなことを試しましたが、期待どおりに動作せず、最終的にテスト スクリプトを誤って上書きしてしまいました。ただしls
、、、while
またはsed
が多数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
-ingプロセス後もファイルが残っているかどうかは記録されますrename
。ファイルがまだ存在する場合は、何らかの理由で名前を変更できなかったため、次の反復で試行されます。一方、ファイルの名前変更に成功した場合は、コマンド ライン引数リストからそのファイルを削除して、次の反復をこのファイルにpat/repl
適用しません。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
、その行をループして、変数$pattern
と$replacement
変数を埋めます。2 番目のステップでは、./files
現在のパターンに一致するディレクトリ内のすべてのファイルが検索されます。2 番目のパターンが一致したときに、再度ファイル名を変更しようとすると失敗するため、これを実行する必要が生じます。最後に、シェル パラメータ置換を使用して、ファイル自体の名前のみを変更し、ファイルを含むディレクトリの名前は変更しません。
うまくいかないのは、大文字と小文字を区別せずに一致するものを置き換えることですが、私はそれで生きていけると思います。
答え4
覚えておくべき重要な点は、ディレクトリ ツリーのトラバースは時間のかかるプロセスであるため、1 回だけ実行されるということです。まず、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
' {} {} \;