
複数のファイル名を一覧表示し、入力名ごとに新しい名前を出力するfind
ユーティリティ(と呼びますutil
)を介してパイプし、各ファイルの名前を古い名前から新しい名前に変更したいと思います。
最も基本的な解決策は次のとおりです。
find . -print0 | while IFS= read -d '' -r old_name; do
new_name="$(echo "$file" | util)"
mv "$old_name" "$new_name"
done
このアプローチの問題は、util
各ファイル名を個別に実行するには遅すぎるということです。したがって、解決策はutil
一度だけ起動し、この単一のプロセスを介してすべてのファイル名を転送することです。
find . -print0 >old_names
util <old_names >new_names
exec {old_fd}<old_names
exec {new_fd}<new_names
while IFS= read -d '' -r old_name <&$old_fd &&
IFS= read -d '' -r new_name <&$new_fd; do
mv "$old_name" "$new_name"
done
これはutil
一度だけ始まりますが、もはやパイプではありません。すべてのファイルをtmpファイルにリストし、util
このtmpファイルを実行して別のtmpファイルをインポートしてから、実際に名前変更を開始する必要があります。 。
パイプライン方式でこれを行うために、次のことを試しました。
mkfifo old_names new_names
find . -print0 | tee old_names | util >new_names &
exec {old_fd}<old_names
exec {new_fd}<new_names
while IFS= read -d '' -r old_name <&$old_fd &&
IFS= read -d '' -r new_name <&$new_fd; do
mv "$old_name" "$new_name"
done
util
残念ながら、これは入力/出力がバッファリングされる方法によってはデッドロックになる可能性があります。
だから私の質問は:bashでこれを行う正しい方法は何ですか?
答え1
パイプを接続しないソリューション:
find . -print > infiles
cat infiles | util > outfiles
parallel mv ::::+ infiles outfiles
利点:とても簡単です。短所:名前の変更はutil
完了後にのみ開始されます。一時ファイル2個。
find . -print > infiles
cat infiles | util | parallel -j1 mv ::::+ infiles -
利点:命名がutil
開始されると、名前の変更も開始されます。短所:一時ファイルが1つです。
find . -print | util | parallel -j1 mv ::::+ <(find . -print) -
利点:命名がutil
開始されると、名前の変更も開始されます。欠点:現在のディレクトリを変更する必要はありません。そうしないと、両方のfind
sが同じ結果を提供しない可能性があります。
答え2
変換の複雑さに応じて、名前の変更(Larry Wallの名前の変更)を使用できます。
または
util
出力名のペアを並べ替えます:old-name
new-name
、次に
find . -print0 | util | xargs -n2 mv -T
または
これを試してみてください。
function a() {
echo -n "$1 "
echo "$1" | sed -e s/a/b/g
}
… | while read arg; do
a "$arg"
done