web-dev-qa-db-ja.com

bash:バックグラウンドで複数の連鎖コマンドを開始

私は、bashを使用して、並列で、バックグラウンドでいくつかのコマンドを実行しようとしています。これが私がやろうとしていることです:

forloop {
  //this part is actually written in Perl
  //call command sequence
  print `touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;`;
}

バッククォート( ``)の間の部分は、新しいシェルを生成し、コマンドを連続して実行します。つまり、元のプログラムへの制御は、最後のコマンドが実行された後にのみ戻ります。バックグラウンドでステートメント全体を実行したいので(出力/戻り値は期待していません)、ループを実行し続けたいです。

呼び出し元のプログラム(ループがあるプログラム)は、生成されたシェルがすべて終了するまで終了しません。

Perlのスレッドを使用して、さまざまなシェルを呼び出すさまざまなスレッドを生成することもできますが、それはやり過ぎのようです...

シェルを起動して、一連のコマンドを与え、バックグラウンドに移動するように指示できますか?

44
Mad_Ady

私はこれをテストしていませんが、どうですか

print `(touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;) &`;

括弧はサブシェルで実行することを意味しますが、それは害になりません。

30
Hugh Allen

ヒュー、ありがとう。

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:~$ 

他のアイデアは、コマンドシーケンスではなく各コマンドをバックグラウンドで開始するため機能しません(これは私の場合は重要です!)。

ありがとうございました!

20
Mad_Ady

別の方法は、次の構文を使用することです。

{ command1; command2; command3; } &
wait

&は、各コマンドの後ではなく、コマンドグループの最後に移動します。最初のブラケットの後、最後のブラケットの前のスペースと同様に、最後のコマンドの後のセミコロンが必要です。最後のwaitは、生成された子プロセス(コマンドグループ)が終了する前に親プロセスが強制終了されないようにします。

stderrstdoutのリダイレクトのような派手なこともできます:

{ 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
18
lechup
for command in $commands
do
    "$command" &
done
wait

コマンドの最後にあるアンパサンドはバックグラウンドで実行され、waitはバックグラウンドタスクが完了するまで待機します。

14
GavinCattell

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ロックファイルの代わりにコピー/名前変更戦略を検討します(しかしそれは別のトピックです)。

5
NVRAM

探している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
3
slm

ここでこのスレッドを見つけて、バックグラウンドジョブとしてチェーンステートメントを生成するコードスニペットをまとめることにしました。これを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"

2
RAKK

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

結果はメールでアカウントに送信されます。

2
Eliseo Carrasco

サブシェルでコマンドを実行します。

(command1 ; command2 ; command3) &
2
Radu Simionescu

次のように、&sを使用してコマンドを中括弧で囲みます。

{command1 & ; command2 & ; command3 & ; }

これはサブシェルを作成しませんが、バックグラウンドでコマンドのグループを実行します。

HTH

1
Zsolt Botykai

誰も適切な解決策に答えなかった理由がわかりません:

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 "の出力

1
ephemient

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

1
Nikolas Britton

誰かがまだ興味を持っている場合に備えて、次のようなサブシェルを呼び出さなくても実行できます。

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オプションは、進行中のプロセスの詳細な統計を提供します。そのため、プロセスの完了数と完了までにかかる時間を知ることができます。

0
Kannan Mohan

パラメーターをコマンドグループ(シーケンシャルコマンドを持つ)に渡し、バックグラウンドで実行できます。

for hrNum in {00..11};
do
    oneHour=$((10#$hrNum + 0))
    secondHour=$((10#$hrNum + 12))
    { echo "$oneHour"; echo "$secondHour"; } &
    wait
done
0
user7668