Bash:デッドロックなしでパイプを接続する

Bash:デッドロックなしでパイプを接続する

複数のファイル名を一覧表示し、入力名ごとに新しい名前を出力する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開始されると、名前の変更も開始されます。欠点:現在のディレクトリを変更する必要はありません。そうしないと、両方のfindsが同じ結果を提供しない可能性があります。

おすすめ記事