web-dev-qa-db-ja.com

n個以下の並列サブシェルを実行する方法

メインスクリプトから添え字を実行しようとしていますが、n以下で実行されることを確認したい同時に。

次の簡略化した例を示します。

各添え字は、RAM(/dev/shm/)に一意のタイムスタンプで作成された名前のダミーファイルを作成し、完了したら削除します。

メインスクリプトは、添え字に由来する/dev/shm/内のダミーファイルの数をカウントし、2つ以上のダミーファイルがすでに実行されている場合、新しい添え字を起動しません(起動すべきではありません)。

ただし、メインスクリプトはwhile条件を無視しているようで、5つの添え字すべてを一度に起動します。

私のコードの何が問題になっていますか?

mainscript.txt

#!/bin/bash
for counter in $(seq 1 5)
do
        while [ $(ls -1 /dev/shm/|grep "script044"|wc -l) -ge 2 ]
        do
                sleep 0
        done

        xterm -e "bash script044.txt" &
done

exit

script044.txt(添え字)

#!/bin/bash

tempfilename="script044_"$(date +%Y%m%d%H%M%S)_${RANDOM}
echo > /dev/shm/${tempfilename}

for counter in $(seq 1 $(shuf -i 10-45 -n 1))
do
        sleep 1
        printf "${counter}\r"
done

rm /dev/shm/${tempfilename}

exit
3

(コンベンション-.txtは単なるプレーンテキストファイルです。 .shファイルはシェルスクリプトファイルです。).

Mainscript.txtスクリプトに競合状態があります。具体的には、whileループは、script044.txtスクリプトが一時ファイルを作成できるようになる前に次の反復を開始します。実際、これらのファイルが作成される前に、ループ全体が繰り返されます。

この種のことを処理するためのより堅牢な方法は、一時ファイルを忘れて、代わりにシェル組み込みのwaitを使用することです。

#!/bin/bash

pid_count=0
for counter in $(seq 1 5)
do
    xterm -e "bash script044.txt" &
    if (( ++pid_count > 2 )); then
        wait -n
        ((pid_count--))
    fi
done

これにより、サブプロセスが開始されるたびにカウンターがインクリメントされます。カウンターが3より大きい場合、次のサブプロセスを終了するためにwaitします。 waitが戻ったら、カウンターをデクリメントし、もう一度回って次のxtermを開始します。

Script044.txtからすべてのtempfilename関連の行を削除できます-それらは不要になりました。


@chepnerが指摘しているように、必要な-nオプションはbash4.3以降でのみ使用できます。

3
Digital Trauma

シェルのみのソリューションを目指している場合は役に立ちませんが、GNU parallelは、この正確な解決に役立つ sem コマンドを提供します状況。

次の(スクリプトがないため、テストされていません)は、ジョブを5回実行する必要がありますが、一度に2回だけ実行し、最後に終了するのを待ちます。

LIMIT=2
for i in {1..5}; do
    sem -j $LIMIT 'term -e "bash script044.txt"'
done
sem --wait
2
Steven D

IPCが必要なようです。スリープをループして毎回テストを実行するのではなく、子プロセスがいつ完了したかを通知するのを待つことができます。これがパイプの目的です。

子プロセスを親に報告させることができます。パイプを開いて、彼らと共有します。彼らが終わったとき、彼らはただ親に知らせる必要があります。

_sub()(  trap "echo >&9" 0
        sleep 5
)
eval    "exec 9<>"<(echo);i=0
until   [ "$((i+=1))" -gt 5 ]
do      sub & read na <&9
        date +%S:%t"$i"
done
_

プロセス置換を使用して開きます。シェルでこれを実行できない場合は、代わりに次を使用できます。

_mkfifo pipe; exec 9<>pipe; rm pipe; echo >&9
_

これで、最初のechoがパイプに1行を配置します-どちらの場合も。これにより、最初から1つの待機プロセスが先行します。つまり、2つの並行プロセスをすべて実行します。このスクリプトは、dateを使用して、各sub()呼び出し間の秒数をレポートします。出力は次のとおりです。

_34: 1
39: 2
39: 3
44: 4
44: 5
_

そこ。ご覧のとおり、5秒ごとに子が死亡し、その場合、echoreadが現在ブロックしているパイプへの行です。 read findが入力の改行になるとすぐに、実行中の処理を終了して最初からやり直すことができます。

Script_044にtrapを入れる必要があります。これは、子プロセスが終了したときに父親に呼び出すように指示します。

1
mikeserv

これを実現する一般的な方法は、xargsを使用することです。

printf "%s\n" {1..5} | xargs -P2 -n1 -i xterm -e '/bin/echo Job {}; bash'

printfは、xargsに値を入力するためだけにあります。 -iフラグが使用されている場合、xargsのデフォルトのreplace-strは{}です。詳細については、xargsのマンページをお読みください。これは、GNUツールとシェルとしてのbashで機能するはずです。

xargs flags: 
-P     Run up to max-procs processes at a time.[…]
-n     Use  at most max-args arguments per command line.[…]
-i/-I  Replace occurrences of replace-str in the initial-arguments with names read from standard input.[…]
1
zstegi

xterm ..の新しいインスタンスを起動した直後に遅延を挿入します。

        xterm -e "bash script044.txt" &
        sleep 0.1
0
yaegashi

GNU Parallelを使用すると、次のようになります。

seq 1 5 | parallel -j2 -N0 'xterm -e "bash script044.txt"'

GNU Parallelは一般的な並列処理機能であり、同じマシン上またはsshアクセスできる複数のマシン上でジョブを簡単に並列実行できます。多くの場合、forループを置き換えることができます。

4つのCPUで実行する32の異なるジョブがある場合、並列化する簡単な方法は、各CPUで8つのジョブを実行することです。

Simple scheduling

代わりに、GNU Parallelは、終了時に新しいプロセスを生成します。つまり、CPUをアクティブに保ち、時間を節約します。

GNU Parallel scheduling

インストール

GNU Parallelがディストリビューション用にパッケージ化されていない場合は、rootアクセスを必要としない個人インストールを実行できます。これを行うことで10秒で実行できます。

(wget -O - pi.dk/3 || curl pi.dk/3/ || fetch -o - http://pi.dk/3) | bash

その他のインストールオプションについては、 http://git.savannah.gnu.org/cgit/parallel.git/tree/README を参照してください。

詳細

その他の例を参照してください: http://www.gnu.org/software/parallel/man.html

イントロビデオを見る: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

チュートリアルをウォークスルーします: http://www.gnu.org/software/parallel/parallel_tutorial.html

サポートを受けるためにメーリングリストにサインアップしてください: https://lists.gnu.org/mailman/listinfo/parallel

0
Ole Tange