私は、bashを使用して、並列で、バックグラウンドでいくつかのコマンドを実行しようとしています。これが私がやろうとしていることです:
forloop {
//this part is actually written in Perl
//call command sequence
print `touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;`;
}
バッククォート( ``)の間の部分は、新しいシェルを生成し、コマンドを連続して実行します。つまり、元のプログラムへの制御は、最後のコマンドが実行された後にのみ戻ります。バックグラウンドでステートメント全体を実行したいので(出力/戻り値は期待していません)、ループを実行し続けたいです。
呼び出し元のプログラム(ループがあるプログラム)は、生成されたシェルがすべて終了するまで終了しません。
Perlのスレッドを使用して、さまざまなシェルを呼び出すさまざまなスレッドを生成することもできますが、それはやり過ぎのようです...
シェルを起動して、一連のコマンドを与え、バックグラウンドに移動するように指示できますか?
私はこれをテストしていませんが、どうですか
print `(touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;) &`;
括弧はサブシェルで実行することを意味しますが、それは害になりません。
ヒュー、ありがとう。
adrianp@frost:~$ (echo "started"; sleep 15; echo "stopped")
started
stopped
adrianp@frost:~$ (echo "started"; sleep 15; echo "stopped") &
started
[1] 7101
adrianp@frost:~$ stopped
[1]+ Done ( echo "started"; sleep 15; echo "stopped" )
adrianp@frost:~$
他のアイデアは、コマンドシーケンスではなく各コマンドをバックグラウンドで開始するため機能しません(これは私の場合は重要です!)。
ありがとうございました!
別の方法は、次の構文を使用することです。
{ command1; command2; command3; } &
wait
&
は、各コマンドの後ではなく、コマンドグループの最後に移動します。最初のブラケットの後、最後のブラケットの前のスペースと同様に、最後のコマンドの後のセミコロンが必要です。最後のwait
は、生成された子プロセス(コマンドグループ)が終了する前に親プロセスが強制終了されないようにします。
stderr
とstdout
のリダイレクトのような派手なこともできます:
{ command1; command2; command3; } 2>&2 1>&1 &
あなたの例は次のようになります。
forloop() {
{ touch .file1.lock; cp bigfile1 /destination; rm .file1.lock; } &
}
# ... do some other concurrent stuff
wait # wait for childs to end
for command in $commands
do
"$command" &
done
wait
コマンドの最後にあるアンパサンドはバックグラウンドで実行され、wait
はバックグラウンドタスクが完了するまで待機します。
GavinCattell 最も近い(bash、IMOの場合)が、Mad_Adyが指摘したように、「ロック」ファイルを処理しません。これは:
他の保留中のジョブがある場合、waitもそれらを待機します。 onlyコピーを待つ必要がある場合、それらのPIDを蓄積して、それらだけを待つことができます。そうでない場合は、「pids」を使用して3行を削除できますが、より一般的です。
さらに、コピーを完全に回避するためにチェックを追加しました。
pids=
for file in bigfile*
do
# Skip if file is not newer...
targ=/destination/$(basename "${file}")
[ "$targ" -nt "$file" ] && continue
# Use a lock file: ".fileN.lock" for each "bigfileN"
lock=".${file##*/big}.lock"
( touch $lock; cp "$file" "$targ"; rm $lock ) &
pids="$pids $!"
done
wait $pids
ちなみに、新しいファイルをFTPリポジトリ(または同様のもの)にコピーしているようです。もしそうなら、あなたはcouldロックファイルの代わりにコピー/名前変更戦略を検討します(しかしそれは別のトピックです)。
探しているbashの機能はCompound Commands
。詳細については、manページを参照してください。
複合コマンド複合コマンドは次のいずれかです。
(list) list is executed in a subshell environment (see COMMAND EXECUTION ENVIRONMENT below). Variable assignments and builtin commands that affect the Shell's environment do not remain in effect after the command completes. The return status is the exit status of list. { list; } list is simply executed in the current Shell environment. list must be terminated with a newline or semicolon. This is known as a group command. The return status is the exit status of list. Note that unlike the metacharac‐ ters ( and ), { and } are reserved words and must occur where a reserved Word is permitted to be recognized. Since they do not cause a Word break, they must be separated from list by whitespace or another Shell metacharac‐ ter.
他にもありますが、これらはおそらく2つの最も一般的なタイプです。最初の括弧はサブシェルで一連のコマンドのリストを実行し、2番目の括弧は現在のシェルで一連のコマンドのリストを実行します。
% ( date; sleep 5; date; )
Sat Jan 26 06:52:46 EST 2013
Sat Jan 26 06:52:51 EST 2013
% { date; sleep 5; date; }
Sat Jan 26 06:52:13 EST 2013
Sat Jan 26 06:52:18 EST 2013
ここでこのスレッドを見つけて、バックグラウンドジョブとしてチェーンステートメントを生成するコードスニペットをまとめることにしました。これをLinuxのBASH、IBM AIXのKSH、AndroidのBusyboxのASHでテストしたので、any Bourne-like Shellで動作すると言っても安全だと思います。
processes=0;
for X in `seq 0 10`; do
let processes+=1;
{ { echo Job $processes; sleep 3; echo End of job $processes; } & };
if [[ $processes -eq 5 ]]; then
wait;
processes=0;
fi;
done;
このコードは、同時ジョブの一定の制限までの数のバックグラウンドジョブを実行します。たとえば、これを使用して、大量のxz
プロセスがメモリ全体を消費せずにコンピューターをスローすることなく、xz
でgzip圧縮された多くのファイルを再圧縮できます。この場合、あなたが使う *
はfor
のリストであり、バッチジョブはgzip -cd "$X" | xz -9c > "${X%.gz}.xz"
。
Atジョブを使用してコマンドを実行します。
# date
# jue sep 13 12:43:21 CEST 2012
# at 12:45
warning: commands will be executed using /bin/sh
at> command1
at> command2
at> ...
at> CTRL-d
at> <EOT>
job 20 at Thu Sep 13 12:45:00 2012
結果はメールでアカウントに送信されます。
サブシェルでコマンドを実行します。
(command1 ; command2 ; command3) &
次のように、&sを使用してコマンドを中括弧で囲みます。
{command1 & ; command2 & ; command3 & ; }
これはサブシェルを作成しませんが、バックグラウンドでコマンドのグループを実行します。
HTH
誰も適切な解決策に答えなかった理由がわかりません:
my @children;
for (...) {
...
my $child = fork;
exec "touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;" if $child == 0;
Push @children, $child;
}
# and if you want to wait for them to finish,
waitpid($_) for @children;
これにより、Perlは子を生成して各コマンドを実行し、すべての子が完了するのを待ってから続行できます。
ところで、
print `some command`
そして
system "some command"
同じ内容を標準出力に出力しますが、最初のオーバーヘッドは、Perlがすべての「some command
"の出力
Forループの分岐:
for i in x; do ((a; b; c;)&); done
例:
for i in 500 300 100; do ((printf "Start $i: "; date; dd if=/dev/zero of=testfile_$i bs=1m count=$i 2>/dev/null; printf "End $i: "; date;)&) && sleep 1; done
誰かがまだ興味を持っている場合に備えて、次のようなサブシェルを呼び出さなくても実行できます。
print `touch .file1.lock && cp bigfile1 /destination && rm .file1.lock &`;
GNU parallel
コマンドを使用して、ジョブを並行して実行できます。より安全で高速です。
私の推測では、ソースから宛先に複数の大きなファイルをコピーしようとしているのでしょう。そしてそのためには、以下のステートメントと並行してそれを行うことができます。
$ ls *|parallel -kj0 --eta 'cp {} /tmp/destination'
-j0
オプションを使用したため、すべてのファイルが並行してコピーされます。並列プロセスの数を減らす必要がある場合は、-j<n>
を使用できます。ここで、<n>
は実行する並列プロセスの数です。
また、Parallelはプロセスの出力を収集し、他のジョブ制御メカニズムでは実行できない連続的な方法(-k
オプション付き)でレポートします。
--eta
オプションは、進行中のプロセスの詳細な統計を提供します。そのため、プロセスの完了数と完了までにかかる時間を知ることができます。
パラメーターをコマンドグループ(シーケンシャルコマンドを持つ)に渡し、バックグラウンドで実行できます。
for hrNum in {00..11};
do
oneHour=$((10#$hrNum + 0))
secondHour=$((10#$hrNum + 12))
{ echo "$oneHour"; echo "$secondHour"; } &
wait
done