シェルスクリプトの実行を同期する
-
16-09-2019 - |
質問
の修正バージョン シェルスクリプト オーディオ ファイルを FLAC から MP3 形式に変換します。このコンピューターにはクアッドコア CPU が搭載されています。スクリプトは以下を使用して実行されます。
./flac2mp3.sh $(find flac -type f)
これにより、FLAC ファイルが変換されます。 flac
ディレクトリ (ファイル名にスペースは含まれません) を MP3 ファイルにコピーします。 mp3
ディレクトリ (と同じレベル) flac
)。宛先の MP3 ファイルがすでに存在する場合、スクリプトはそのファイルをスキップします。
問題は、スクリプトの 2 つのインスタンスがほぼ同時に同じ MP3 ファイルの存在をチェックすることがあり、結果として MP3 ファイルが破損してしまうことです。
各コマンドラインで異なるファイルセットを指定したり、作業を上書きしたりせずに、スクリプトを複数回 (つまり、コアごとに 1 回) 実行するにはどうすればよいでしょうか?
アップデート - 最小限の競合状態
このスクリプトは次のロック メカニズムを使用します。
# Convert FLAC to MP3 using tags from flac file.
#
if [ ! -e $FLAC.lock ]; then
touch $FLAC.lock
flac -dc "$FLAC" | lame${lame_opts} \
--tt "$TITLE" \
--tn "$TRACKNUMBER" \
--tg "$GENRE" \
--ty "$DATE" \
--ta "$ARTIST" \
--tl "$ALBUM" \
--add-id3v2 \
- "$MP3"
rm $FLAC.lock
fi;
ただし、それでも競合状態が残ります。
解決
「lockfile」コマンドは、競合状態を発生させずにシェル スクリプトに対して実行しようとしている内容を提供します。このコマンドは、特にこの種の目的のために procmail 担当者によって作成され、ほとんどの BSD/Linux システムで利用できます (procmail はほとんどの環境で利用できるため)。
テストは次のようになります。
lockfile -r 3 $FLAC.lock
if test $? -eq 0 ; then
flac -dc "$FLAC" | lame${lame_opts} \
--tt "$TITLE" \
--tn "$TRACKNUMBER" \
--tg "$GENRE" \
--ty "$DATE" \
--ta "$ARTIST" \
--tl "$ALBUM" \
--add-id3v2 \
- "$MP3"
fi
rm -f $FLAC.lock
あるいは、lockfile が無期限に再試行を続けるようにすることもできるため、戻りコードをテストする必要がなく、代わりに出力ファイルをテストして flac を実行するかどうかを判断できます。
他のヒント
持っていない場合 lockfile
そしてそれをインストールすることはできません(どのバージョンでも - いくつかの実装があります)堅牢で移植可能なアトミックミューテックスは mkdir
.
作成しようとしているディレクトリがすでに存在する場合は、 mkdir
失敗するので、それを確認できます。作成が成功すると、コードと同時に他の連携プロセスがクリティカル セクションに存在しないことが保証されます。
if mkdir "$FLAC.lockdir"; then
# you now have the exclusive lock
: critical section
: code goes here
rmdir "$FLAC.lockdir"
else
: nothing? to skip this file
# or maybe sleep 1 and loop back and try again
fi
完全を期すために、おそらく次のものも探してください flock
それが確実に利用できる一連のプラットフォームを使用していて、パフォーマンスの高い代替手段が必要な場合 lockfile
.
作業中の FLAC ファイルのロックを実装できます。何かのようなもの:
if (not flac locked)
lock flac
do work
else
continue to next flac
出力を一意の名前を持つ一時ファイルに送信し、ファイルの名前を目的の名前に変更します。
flac -dc "$FLAC" | lame${lame_opts} \
--tt "$TITLE" \
--tn "$TRACKNUMBER" \
--tg "$GENRE" \
--ty "$DATE" \
--ta "$ARTIST" \
--tl "$ALBUM" \
--add-id3v2 \
- "$MP3.$$"
mv "$MP3.$$" "$MP3"
競合状態がファイル ロック システムを通じて時々漏洩したとしても、最終出力は依然として 1 つのプロセスの結果になります。
ファイル プロセスをロックするには、.lock 拡張子が付いた同じ名前のファイルを作成します。
エンコードを開始する前に、.lock ファイルの存在を確認し、必要に応じて、ロックファイルの日付が古すぎないことを確認します (プロセスが停止した場合に備えて)。存在しない場合は、エンコードの開始前に作成し、エンコードの完了後に削除します。
ファイルをフロックすることもできますが、これは flock() を呼び出してファイルに書き込み、閉じてロックを解除する c でのみ機能します。シェル スクリプトの場合は、ファイルの書き込みを行うために別のユーティリティを呼び出している可能性があります。
Makefileを書いてみてはどうでしょうか?
ALL_FLAC=$(wildcard *.flac)
ALL_MP3=$(patsubst %.flac, %.mp3, $(ALL_FLAC)
all: $(ALL_MP3)
%.mp3: %.flac
$(FLAC) ...
それならそうする
$ make -j4 all
bash では、ファイルの上書きを避けるために noclobber オプションを設定することができます。
設定をヘルプ| egrep 'noclobber | -c'
次のようなツールを使用します FLOM (フリーロックマネージャー) そして、以下のようにコマンドをシリアル化するだけです。
flom -- flac ....