どのようにしてbashスクリプトから複数のプログラムを並行して実行しますか?


245

多くのプログラムを同時に実行する.shファイルを書き込もうとしています

私はこれを試しました

prog1 
prog2

しかし、これはprog1を実行し、prog1が終了するまで待機してから、prog2を開始します...

では、どうすればそれらを並行して実行できますか?

回答:


216
prog1 &
prog2 &

49
忘れないでくださいwait!はい、bashでは、スクリプトの子プロセスを待つことができます。
Dummy00001

5
別のオプションはnohup、シェルがハングアップしたときにプログラムが強制終了されるのを防ぐために使用することです。
Philipp

@liang:はい、3つ以上のプログラムでも動作します。
psmears

302

どうですか:

prog1 & prog2 && fg

この意志:

  1. を起動しprog1ます。
  2. バックグラウンドに送信しますが、出力は印刷し続けます。
  3. を起動しprog2フォアグラウンドに置いておくと、で閉じることができますctrl-c
  4. あなたが近い場合にはprog2、あなたがに戻りますprog1フォアグラウンドあなたにも近いことでできるようにし、ctrl-c

9
終了prog1時に簡単に終了する方法はありprog2ますか?考える node srv.js & cucumberjs
JP

20
試してみたところ、期待したとおりに動作しませんでした。ただし、わずかな変更が機能しました。 prog1 & prog2 ; fg これは、複数のSSHトンネルを一度に実行するためのものでした。これが誰かを助けることを願っています。
jnadro52 2016年

2
@ jnadro52ソリューションには、prog2すぐに実行できない場合prog1、フォアグラウンドに戻るという効果があります。これが望ましい場合は、問題ありません。
Ory Band

3
SSHシェルでこのようなコマンドを実行すると、prog1を強制終了するのが難しくなります。Ctrl-cが機能しませんでした。端末全体を強制終了しても、prog1は実行されたままになります。
mercury0114

14
@ jnadro52両方のプロセスを一度に終了する方法はprog1 & prog2 && kill $!です。
zaboco

79

使用できますwait

some_command &
P1=$!
other_command &
P2=$!
wait $P1 $P2

バックグラウンドプログラムのPIDを変数に割り当て($!最後に起動されたプロセスのPIDです)、waitコマンドはそれらを待ちます。スクリプトを強制終了すると、プロセスも強制終了されるので便利です。


4
私の経験では、待機を強制終了しても、他のプロセスも強制終了されません。
Quinn Comendant 2018

1
ループでバックグラウンドプロセスを開始している場合、すべてのバックグラウンドプロセスが完了するのを待ってから、次のコマンドセットの実行に進むことができます。 #!/usr/bin/env bash ARRAY='cat bat rat' for ARR in $ARRAY do ./run_script1 $ARR & done P1=$! wait $P1 echo "INFO: Execution of all background processes in the for loop has completed.."
Yash

@YashプロセスIDを配列に保存してから、配列の待機を呼び出すことができると思います。${}文字列リストなどに補間するために使用する必要があると思います。
trusktr 2018年

最良の答えです。私にとってスクリプトを強制終了すると、プロセスも強制終了されます。macOSカタリナ、zshコンソール
Michael Klishevich

67

GNU Parallel http://www.gnu.org/software/parallel/を使用すると、次のように簡単です。

(echo prog1; echo prog2) | parallel

または、必要に応じて:

parallel ::: prog1 prog2

もっと詳しく知る:


4
parallel構文が異なるの異なるバージョンがあることに注意してください。たとえば、Debian派生物では、moreutilsパッケージにparallelはまったく異なる動作をすると呼ばれる別のコマンドが含まれています。
Joel Cross

4
parallel使用してより良いですか&
Optimus Prime

2
@OptimusPrimeそれは本当に依存します。GNU Parallelはある程度のオーバーヘッドをもたらしますが、見返りに、実行中のジョブと出力をはるかに制御できます。2つのジョブが同時に印刷する場合、GNU Parallelは出力が混合されないようにします。
Ole

1
@OptimusPrime parallelは、コアよりも多くのジョブがある場合に優れ&ています。その場合、コアごとに複数のジョブを同時に実行します。(鳩の巣の原理を参照)
ジェレミア

2
@OleTange「あなたのコマンドラインはそれのためにあなたを愛するでしょう。」私もそうです。☺–
ジェレミア

55

で複数のプロセスを簡単に実行および強制終了できるようにしたい場合ctrl-c、これは私のお気に入りの方法です。(…)サブシェルで複数のバックグラウンドプロセスを生成し、トラップSIGINTを実行kill 0して、サブシェルグループで生成されたすべてを強制終了します。

(trap 'kill 0' SIGINT; prog1 & prog2 & prog3)

複雑なプロセス実行構造を持つことができ、すべてが1つで終了しますctrl-c(最後のプロセスがフォアグラウンドで実行されていることを確認してください。つまり、&afterを含めないでくださいprog1.3)。

(trap 'kill 0' SIGINT; prog1.1 && prog1.2 & (prog2.1 | prog2.2 || prog2.3) & prog1.3)

これは断然最良の答えです。
ニック

10

xargs -P <n><n>コマンドを並行して実行できます。

一方で-P、非標準オプションで、GNU(Linuxの)およびMacOSの/ BSDの実装の両方がそれをサポートしています。

次の例:

  • 走る最大で、一度に並列に3つのコマンドを
  • 以前に起動されたプロセスが終了したときにのみ追加のコマンドが開始されます。
time xargs -P 3 -I {} sh -c 'eval "$1"' - {} <<'EOF'
sleep 1; echo 1
sleep 2; echo 2
sleep 3; echo 3
echo 4
EOF

出力は次のようになります。

1   # output from 1st command 
4   # output from *last* command, which started as soon as the count dropped below 3
2   # output from 2nd command
3   # output from 3rd command

real    0m3.012s
user    0m0.011s
sys 0m0.008s

タイミングは、コマンドが並行して実行されたことを示しています(最後のコマンドは、最初の3つのうち最初のコマンドが終了した後にのみ起動されましたが、非常に迅速に実行されました)。

xargsすべてのコマンドが終了するまで、コマンド自体は戻りませんが、あなたはコントロールオペレータでそれを終了させることによって、バックグラウンドでそれを実行することができ&、その後、使用してwait全体を待つために、組み込みのxargs仕上げにコマンド。

{
  xargs -P 3 -I {} sh -c 'eval "$1"' - {} <<'EOF'
sleep 1; echo 1
sleep 2; echo 2
sleep 3; echo 3
echo 4
EOF
} &

# Script execution continues here while `xargs` is running 
# in the background.
echo "Waiting for commands to finish..."

# Wait for `xargs` to finish, via special variable $!, which contains
# the PID of the most recently started background process.
wait $!

注意:

  • BSD / macOSではxargs、並列で実行するコマンドの数を明示的に指定する必要がありますが、GNU xargsでは、できるだけ-P 0多くのコマンドを並列で実行するように指定できます。

  • 並行し実行されるプロセスからの出力は、生成されるときに到着するため、予期せずインターリーブされます。

    • Oleの回答(ほとんどのプラットフォームには標準で付属していませ)でparallel述べたように、GNU はプロセスごとに出力を便利にシリアル化(グループ化)し、より多くの高度な機能を提供します。

9
#!/bin/bash
prog1 & 2> .errorprog1.log; prog2 & 2> .errorprog2.log

エラーを別のログにリダイレクトします。


13
リダイレクトの後にアンパサンドを置き、セミコロンを省く必要があります(アンパサンドはコマンドセパレーターの機能も実行します):prog1 2> .errorprog1.log & prog2 2> .errorprog2.log &
通知があるまで一時停止します。

セミコロンは両方のコマンドを実行します。debashをテストして、うまく機能することを確認できます;)例:pwd&2> .errorprog1.log; echo "wop"&2> .errorprog2.logプログラムをバックグラウンドに置き、プログラムをバックグラウンドに置いて、すぐに次のコマンドを実行した場合。
フェルミン

2
それは機能しません-エラーはファイルにリダイレクトされません。試してください:ls notthere1 & 2> .errorprog1.log; ls notthere2 & 2>.errorprog2.log。エラーはコンソールに送られ、両方のエラーファイルは空です。@Dennis Williamsonが言う&ように;、セパレーターはのように、(a)コマンドの最後に(リダイレクトの後に)移動する必要があり、(b)は必要ない;:-)
psmears

8

nohupを呼び出す非常に便利なプログラムがあります。

     nohup - run a command immune to hangups, with output to a non-tty

4
nohupそれ自体はバックグラウンドで何も実行せず、使用nohupはバックグラウンドでタスクを実行するための要件や前提条件ではありません。多くの場合、これらは一緒に使用すると便利ですが、それだけでは問題の解決にはなりません。
tripleee 2018年

8

これは、最大nプロセスで並列に実行するために使用する関数です(例ではn = 4)。

max_children=4

function parallel {
  local time1=$(date +"%H:%M:%S")
  local time2=""

  # for the sake of the example, I'm using $2 as a description, you may be interested in other description
  echo "starting $2 ($time1)..."
  "$@" && time2=$(date +"%H:%M:%S") && echo "finishing $2 ($time1 -- $time2)..." &

  local my_pid=$$
  local children=$(ps -eo ppid | grep -w $my_pid | wc -w)
  children=$((children-1))
  if [[ $children -ge $max_children ]]; then
    wait -n
  fi
}

parallel sleep 5
parallel sleep 6
parallel sleep 7
parallel sleep 8
parallel sleep 9
wait

max_childrenがコアの数に設定されている場合、この関数はアイドル状態のコアを回避しようとします。


1
素晴らしいスニペットですが、bashの下に "wait -n"の説明が見つかりません。これは無効なオプションであると表示されています。タイプミスまたは何かを見逃しましたか?
Emmanuel Devaux 2017年

1
@EmmanuelDevaux:wait -n必要とbash4.3以降を、それが待っているにロジックを変更するいかなる終了する指定/暗黙のプロセス。
mklement0

タスクの1つが失敗した場合、スクリプトを終了したいと思いますか?
52coder、

@ 52coderを使用して、失敗した子をキャプチャするように関数を調整できます。例: "$ @" && time2 = $(date + "%H:%M:%S")&& echo "finishing $ 2($ time1-$ time2 )... "|| エラー= 1&。次に、「if」部分のエラーをテストし、必要に応じて関数を中止します
-arnaldocan

7

あなたはppssを試すことができます。ppssはかなり強力です。ミニクラスターを作成することもできます。xargs -Pは、やっかいな並列処理のバッチを実行する場合にも役立ちます。


7

最近、同様の状況で複数のプログラムを同時に実行し、それらの出力を別々のログファイルにリダイレクトし、それらが完了するのを待つ必要があり、次のような結果になりました。

#!/bin/bash

# Add the full path processes to run to the array
PROCESSES_TO_RUN=("/home/joao/Code/test/prog_1/prog1" \
                  "/home/joao/Code/test/prog_2/prog2")
# You can keep adding processes to the array...

for i in ${PROCESSES_TO_RUN[@]}; do
    ${i%/*}/./${i##*/} > ${i}.log 2>&1 &
    # ${i%/*} -> Get folder name until the /
    # ${i##*/} -> Get the filename after the /
done

# Wait for the processes to finish
wait

ソース:http : //joaoperibeiro.com/execute-multiple-programs-and-redirect-their-outputs-linux/


4

プロセス生成マネージャー

確かに、技術的にはこれらはプロセスであり、このプログラムは実際にはプロセススポーンマネージャーと呼ばれるべきですが、これはアンパサンドを使用してフォークしたときのBASHの動作方法にのみ起因し、fork()またはclone()システムコールを使用しますこれは、メモリを共有するpthread_create()のようなものではなく、別のメモリ空間に複製されます。BASHが後者をサポートしている場合、各「実行シーケンス」はまったく同じように動作し、より効率的なメモリフットプリントを得ながら従来のスレッドと呼ぶことができます。ただし、機能的には同じように機能しますが、各ワーカークローンではGLOBAL変数を使用できないため、プロセス間通信ファイルと基本的なflockセマフォを使用してクリティカルセクションを管理するため、少し難しくなります。もちろん、BASHからフォークすることが基本的な答えですが、人々はそれを知っているように感じますが、生成されたものを単にフォークして忘れるのではなく、管理することを本当に望んでいます。これは、すべて単一のリソースにアクセスするフォークされたプロセスの最大200のインスタンスを管理する方法を示しています。明らかにこれはやり過ぎですが、書くことを楽しんだので続けました。それに応じて端末のサイズを増やします。これがお役に立てば幸いです。

ME=$(basename $0)
IPC="/tmp/$ME.ipc"      #interprocess communication file (global thread accounting stats)
DBG=/tmp/$ME.log
echo 0 > $IPC           #initalize counter
F1=thread
SPAWNED=0
COMPLETE=0
SPAWN=1000              #number of jobs to process
SPEEDFACTOR=1           #dynamically compensates for execution time
THREADLIMIT=50          #maximum concurrent threads
TPS=1                   #threads per second delay
THREADCOUNT=0           #number of running threads
SCALE="scale=5"         #controls bc's precision
START=$(date +%s)       #whence we began
MAXTHREADDUR=6         #maximum thread life span - demo mode

LOWER=$[$THREADLIMIT*100*90/10000]   #90% worker utilization threshold
UPPER=$[$THREADLIMIT*100*95/10000]   #95% worker utilization threshold
DELTA=10                             #initial percent speed change

threadspeed()        #dynamically adjust spawn rate based on worker utilization
{
   #vaguely assumes thread execution average will be consistent
   THREADCOUNT=$(threadcount)
   if [ $THREADCOUNT -ge $LOWER ] && [ $THREADCOUNT -le $UPPER ] ;then
      echo SPEED HOLD >> $DBG
      return
   elif [ $THREADCOUNT -lt $LOWER ] ;then
      #if maxthread is free speed up
      SPEEDFACTOR=$(echo "$SCALE;$SPEEDFACTOR*(1-($DELTA/100))"|bc)
      echo SPEED UP $DELTA%>> $DBG
   elif [ $THREADCOUNT -gt $UPPER ];then
      #if maxthread is active then slow down
      SPEEDFACTOR=$(echo "$SCALE;$SPEEDFACTOR*(1+($DELTA/100))"|bc)
      DELTA=1                            #begin fine grain control
      echo SLOW DOWN $DELTA%>> $DBG
   fi

   echo SPEEDFACTOR $SPEEDFACTOR >> $DBG

   #average thread duration   (total elapsed time / number of threads completed)
   #if threads completed is zero (less than 100), default to maxdelay/2  maxthreads

   COMPLETE=$(cat $IPC)

   if [ -z $COMPLETE ];then
      echo BAD IPC READ ============================================== >> $DBG
      return
   fi

   #echo Threads COMPLETE $COMPLETE >> $DBG
   if [ $COMPLETE -lt 100 ];then
      AVGTHREAD=$(echo "$SCALE;$MAXTHREADDUR/2"|bc)
   else
      ELAPSED=$[$(date +%s)-$START]
      #echo Elapsed Time $ELAPSED >> $DBG
      AVGTHREAD=$(echo "$SCALE;$ELAPSED/$COMPLETE*$THREADLIMIT"|bc)
   fi
   echo AVGTHREAD Duration is $AVGTHREAD >> $DBG

   #calculate timing to achieve spawning each workers fast enough
   # to utilize threadlimit - average time it takes to complete one thread / max number of threads
   TPS=$(echo "$SCALE;($AVGTHREAD/$THREADLIMIT)*$SPEEDFACTOR"|bc)
   #TPS=$(echo "$SCALE;$AVGTHREAD/$THREADLIMIT"|bc)  # maintains pretty good
   #echo TPS $TPS >> $DBG

}
function plot()
{
   echo -en \\033[${2}\;${1}H

   if [ -n "$3" ];then
         if [[ $4 = "good" ]];then
            echo -en "\\033[1;32m"
         elif [[ $4 = "warn" ]];then
            echo -en "\\033[1;33m"
         elif [[ $4 = "fail" ]];then
            echo -en "\\033[1;31m"
         elif [[ $4 = "crit" ]];then
            echo -en "\\033[1;31;4m"
         fi
   fi
      echo -n "$3"
      echo -en "\\033[0;39m"
}

trackthread()   #displays thread status
{
   WORKERID=$1
   THREADID=$2
   ACTION=$3    #setactive | setfree | update
   AGE=$4

   TS=$(date +%s)

   COL=$[(($WORKERID-1)/50)*40]
   ROW=$[(($WORKERID-1)%50)+1]

   case $ACTION in
      "setactive" )
         touch /tmp/$ME.$F1$WORKERID  #redundant - see main loop
         #echo created file $ME.$F1$WORKERID >> $DBG
         plot $COL $ROW "Worker$WORKERID: ACTIVE-TID:$THREADID INIT    " good
         ;;
      "update" )
         plot $COL $ROW "Worker$WORKERID: ACTIVE-TID:$THREADID AGE:$AGE" warn
         ;;
      "setfree" )
         plot $COL $ROW "Worker$WORKERID: FREE                         " fail
         rm /tmp/$ME.$F1$WORKERID
         ;;
      * )

      ;;
   esac
}

getfreeworkerid()
{
   for i in $(seq 1 $[$THREADLIMIT+1])
   do
      if [ ! -e /tmp/$ME.$F1$i ];then
         #echo "getfreeworkerid returned $i" >> $DBG
         break
      fi
   done
   if [ $i -eq $[$THREADLIMIT+1] ];then
      #echo "no free threads" >> $DBG
      echo 0
      #exit
   else
      echo $i
   fi
}

updateIPC()
{
   COMPLETE=$(cat $IPC)        #read IPC
   COMPLETE=$[$COMPLETE+1]     #increment IPC
   echo $COMPLETE > $IPC       #write back to IPC
}


worker()
{
   WORKERID=$1
   THREADID=$2
   #echo "new worker WORKERID:$WORKERID THREADID:$THREADID" >> $DBG

   #accessing common terminal requires critical blocking section
   (flock -x -w 10 201
      trackthread $WORKERID $THREADID setactive
   )201>/tmp/$ME.lock

   let "RND = $RANDOM % $MAXTHREADDUR +1"

   for s in $(seq 1 $RND)               #simulate random lifespan
   do
      sleep 1;
      (flock -x -w 10 201
         trackthread $WORKERID $THREADID update $s
      )201>/tmp/$ME.lock
   done

   (flock -x -w 10 201
      trackthread $WORKERID $THREADID setfree
   )201>/tmp/$ME.lock

   (flock -x -w 10 201
      updateIPC
   )201>/tmp/$ME.lock
}

threadcount()
{
   TC=$(ls /tmp/$ME.$F1* 2> /dev/null | wc -l)
   #echo threadcount is $TC >> $DBG
   THREADCOUNT=$TC
   echo $TC
}

status()
{
   #summary status line
   COMPLETE=$(cat $IPC)
   plot 1 $[$THREADLIMIT+2] "WORKERS $(threadcount)/$THREADLIMIT  SPAWNED $SPAWNED/$SPAWN  COMPLETE $COMPLETE/$SPAWN SF=$SPEEDFACTOR TIMING=$TPS"
   echo -en '\033[K'                   #clear to end of line
}

function main()
{
   while [ $SPAWNED -lt $SPAWN ]
   do
      while [ $(threadcount) -lt $THREADLIMIT ] && [ $SPAWNED -lt $SPAWN ]
      do
         WID=$(getfreeworkerid)
         worker $WID $SPAWNED &
         touch /tmp/$ME.$F1$WID    #if this loops faster than file creation in the worker thread it steps on itself, thread tracking is best in main loop
         SPAWNED=$[$SPAWNED+1]
         (flock -x -w 10 201
            status
         )201>/tmp/$ME.lock
         sleep $TPS
        if ((! $[$SPAWNED%100]));then
           #rethink thread timing every 100 threads
           threadspeed
        fi
      done
      sleep $TPS
   done

   while [ "$(threadcount)" -gt 0 ]
   do
      (flock -x -w 10 201
         status
      )201>/tmp/$ME.lock
      sleep 1;
   done

   status
}

clear
threadspeed
main
wait
status
echo

0

スクリプトは次のようになります。

prog1 &
prog2 &
.
.
progn &
wait
progn+1 &
progn+2 &
.
.

システムが一度にn個のジョブを実行できると仮定します。一度にn個のジョブのみを実行するには、waitを使用します。


-1

bashjhttps://sourceforge.net/projects/bashj/)、あなたは複数のだけでなく、実行することができますプロセス(他の人が提案方法)だけでなく、複数のスレッドをスクリプトから制御される1つのJVMで。しかしもちろん、これにはJava JDKが必要です。スレッドはプロセスよりも少ないリソースを消費します。

ここに作業コードがあります:

#!/usr/bin/bashj

#!java

public static int cnt=0;

private static void loop() {u.p("java says cnt= "+(cnt++));u.sleep(1.0);}

public static void startThread()
{(new Thread(() ->  {while (true) {loop();}})).start();}

#!bashj

j.startThread()

while [ j.cnt -lt 4 ]
do
  echo "bash views cnt=" j.cnt
  sleep 0.5
done
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.