一部のファイルをコピーするのに時間がかかる状況が発生した場合は、ファイルのコピーに対して並列処理を実行する必要があるとします。たとえば、次のようになります。
for i in ipaddresslist
do
cp x y & //running in back ground or some other process in background
done
wait //will cause till all the files are copied or whatever process if all are finished
これで、wait
すべてのバックグラウンドプロセスが完了しましたが、次のような場合があります。
1)一部のファイルのコピーがより早く発生する可能性があるため、これらのファイルに対していくつかの処理を実行する必要がある場合は、すべてのファイルがコピーされるのを待つ必要があります。
2)レプリケーションプロセス(またはバックグラウンドで実行されている他のプログラム)がログファイルに書き込むと、各バックグラウンドプロセスが同時にファイルに書き込もうとするため、ログファイルが壊れる可能性があります。
この種の問題に対する解決策はありますか? 1) プロセスが完了したことがわかっている場合、プロセスが完了すると、プロセスの特定のインスタンス (ファイルのコピーなど) で残りの処理タスクを開始できます。また、ログファイルの書き込みは順次発生することがあります。
提案してください
答え1
一部のファイルをコピーした後に一部のジョブを開始する必要がある場合は、そのジョブをバックグラウンドジョブの一部として作成してください。
(cp this /there && start job that needs this in /there) &
(cp that /here && start job that needs that in /here) &
wait
(最後は&
必須ではありません。)
これで、より複雑な依存関係にGNUを使用できますmake -j
。
make -j2 -f /dev/fd/3 3<< 'EOF'
all: j1 j2 j3
.PHONY: cp1 cp2 cp3 j1 j2 j3 all
cp1:
cp this /there
cp2:
cp that /here
cp3:
cp this /here
j1: cp1
start job that needs this in /there
j2: cp2
start job that needs that in /here
j3: cp1 cp3
start job that needs this in /here and /there
EOF
-j2
いつでも最大2つのジョブを実行でき、依存関係は尊重されます。
これで、誤ったログファイルを防ぐために2つの主なオプションがあります。
- インターリーブしないでください。つまり、各割り当ての内容を1つずつ追加します。
- どの行がどのタスクに属しているかを簡単に確認できるように、各タスクの各行にラベルを付けて、すばらしくインターリーブされていることを確認してください。
1の場合、最も簡単な方法は、各ジョブ出力を別々のファイルに保存してマージすることです。
(cp this /there && start job that needs this in /there) > j1.log 2>&1 &
(cp that /here && start job that needs that in /here) > j2.log 2>&1 &
wait
cat j1.log j2.log > jobs.log
別のオプションは、パイプラインを使用して各ジョブの出力を収集してマージすることですcat
。で利用可能なシェルプロセスの置き換えは、ksh
これを達成するのに役立ち、zsh
バックbash
グラウンドでも処理できます。
j1() { cp this /there && start job that needs this in /there; }
j2() { cp that /here && start job that needs that in /here; }
cat <(j1 2>&1) <(j2 2>&1) > jobs.log
j1
、同時に開始され、パイプラインと相互接続されますj2
。cat
cat
ただし、2番目のパイプ(作成者)からの読み込みは完了後にのみ開始されますj2
。j1
つまり、j2
パイプサイズ(Linuxでは通常64kiBなど)よりも多くのログレコードが書き込まれると、完了するj2
までブロックされますj1
。
sponge
これはfromを使用して防ぐことができますmoreutils
。たとえば、次のようになります。
cat <(j1 2>&1) <(j2 2>&1 | sponge) > jobs.log
これは、すべての出力がメモリに保存され、catはj2
inが完了した後にのみ出力の書き込みを開始することを意味しますが、この場合、例を使用する方が良いかもしれません。j2
jobs.log
j2
pv -qB 100M
cat <(j1 2>&1) <(j2 2>&1 | pv -qB 100M) > jobs.log
このアプローチは、まだ完了していない場合、j2
出力(および両方のパイプの内容を含む)を記録した後にのみ一時停止し、j1
標準出力への出力が完了するのを待ちません。100M
pv
j2
上記のように、ほとんどのコマンド出力をファイルまたはパイプ(tty以外のすべて)にリダイレクトすると、動作が影響を受けることに注意してください。これらのコマンドまたは呼び出すstdio
API libc
(printf
、、、 ...)はfputs
、fwrite
出力が端末に送信されないことを検出し、これらの標準ミスを実行していない間に大きな出力チャンク(キロバイト)を渡すことによって最適化を実行します。これは、出力とエラーメッセージの順序が影響を受けることを意味します。これが問題になると、GNUシステムまたはFreeBSD(少なくとも)および動的リンクコマンドの場合は、次のものを使用できますstdbuf
。
stdbuf -oL j1 > j1.log 2>&1
変える:
j1 > j1.log 2>&1
stdio出力がラインバッファリングされていることを確認してください(各出力ラインは完了するとすぐに個別に書き込まれます)。
オプション2の場合、PIPE_BUF
バイトより小さいパイプ(Linuxでは4096バイト、ログの平均行よりはるかに大きい)に書き込むことが原子性を保証します。言い換えれば、両方のプロセスが同時に同じパイプに書き込む場合、それらの書き込みは原子性を保証します。両方の書き込みが互いに絡み合わないように保証されます。通常のファイルについてはそのような保証はありませんが、4kiBより小さい2つの書き込みがどのOSやファイルシステムで絡み合うことができるか真剣に疑われます。
したがって、上記のバッファリングなしでログ行が完全に個別に出力される場合、行出力には、このジョブの行の一部であり、他のジョブの一部であるパスがないことが保証されます。
ただし、コマンドが作成中の行の2つの部分(たとえば)の間をフラッシュするのを防ぐ方法はなく、printf("foo"); fflush(stdout); printf("bar\n");
stderrのバッファリングもありません。
もう1つの問題は、すべてのタスクの行がインターリーブされると、どの行がどのタスクに対応するかを知ることが難しいことです。
次の手順で両方の問題を解決できます。
tag() { stdbuf -oL sed "s%^%$1: %"; }
{
j1 2>&1 | tag j1 &
j2 2>&1 | tag j2
} | cat > jobs.log
(誰ももはやパイプに書き込むまで完了しないので、これは必要ありません(ほとんどwait
のシェルでは機能しません)。cat
j1
j2
上記では、| cat
原子性を確保するパイプラインを使用しました。各コマンドの出力をコマンドにパイプします。商標各行には役職があります。必要に応じて出力を作成できます。j1
なぜなら、(タグプレフィックスを持つ)行は全体として個別に出力されるため、出力が破損しないようにするからです。j2
sed
stdbuf -oL
stdbuf -oL
上記と同じ説明が依然として適用される。コマンドには適用されないため、出力をバッファリングする可能性が高いため、生成されてから長い時間が経過するまで記録されない可能性がありますj1
。j2
これは、次の理由で前のケースよりも悪いです。
j1: doing A
j1: doing B
j2: doing C
これは、Bが実行される前にAが実行されることを意味しますj1
が、Cが実行される前に命令を実行するという意味ではありません。したがって、問題が発生した場合は、j2
より多くのコマンドを適用する必要があるかもしれません。stdbuf -oL
このようなシェル機能には適用できませんが、stdbuf
少なくともGNUおよびFreeBSD stdbufでは、これを使用してグローバルまたはサブシェルごとに設定できます。j1
j2
stdbuf
stdbuf_LD_PRELOAD=$(stdbuf sh -c 'export -p LD_PRELOAD')
line_buffered_output() {
eval "$stdbuf_LD_PRELOAD"
export _STDBUF_O=L
}
j1() (line_buffered_output; cp this /there && start...)
答え2
ダウンロードしたすべてのファイルを処理する必要がある場合は、次の手順を実行します。
cp whatever file; process file &
代わりに。
間違ったログファイルが気になる場合は、を使用するか、経由で使用する必要がsyslog(3)
ありますlogger(1)
。nohup(1)