web-dev-qa-db-ja.com

bashで開始されたプログラムの数を制御する

私の研究プロジェクトの一環として、私は大量のデータを多くのファイルに分割して処理しています。

フォルダfoo内のすべてのファイルは、フォルダmyScriptのすべての要素を含むスクリプトbarによって処理される必要があります。

これはmyScriptです:

for f in bar/*
do
    awk 'NR==FNR{a[$0]=$0;next}!a[$0]' $f $1 > tmp
    cp tmp $1
done

Forループですべてのファイルを処理するという最初のアイデアは有効です。

for f in foo/*
do
    ./myScript $f
done

ただし、これには永遠に時間がかかります。 &を追加してバックグラウンドですべてのmyScriptを開始するだけで、大量の入力を伴うawkcpの何千もの並列実行インスタンスが作成されますが、これは明らかに悪いことです。

以下で作成する「スレッド」の数を制限することを考えました

for f in foo/*
do
    THREAD_COUNT=$(ps | wc -f)
    while [ $THREAD_COUNT -ge 12 ]
    do
        sleep 1
        THREAD_COUNT=$(ps | wc -f)
    done
    ./myScript $f &
done

補足:ノードに8つのコアがあり、常にbashpswcが実行されているため、12と比較しています。 ps | wc -lの呼び出し時のヘッダー行。

残念ながら、myScriptを呼び出すと、psに複数のエントリが追加されるため、スクリプトの動作が意図したとおりになりませんでした。

だからここに私の質問があります:もっと簡単な方法はありますか?より安定した方法は?

私はノードで他に何もしていないので、起こっていることはすべてスクリプトによってのみ引き起こされます。

2
stefan

シェルスクリプトを使用してこれを行うことはできますが、これは困難な方法です。シェルスクリプトは、複数のバックグラウンドジョブの操作があまり得意ではありません。

GNU make または-jオプションを持つ他のバージョンのmakeを使用して、複数のジョブを並行して実行することをお勧めします。各サブタスクをメイクファイルルールとして記述します。

以下のmakefileスニペットはあなたのルールを実装していると思いますが、あなたのコードは従うのが難しかったので、私は今それを正しく理解しているかもしれません。最初の行は、入力ファイルからの出力ファイルを列挙します(注:入力ファイルを上書きしないでください!何らかの理由でジョブが途中で停止すると、処理されたかどうかわからないデータになってしまいます) 。インデントされた行は、実行するコマンドです。タブを使用して、8つのスペースではなく、各コマンドをインデントします。これらのコマンドで、$<はソースファイル(.inファイル)を表し、$@はターゲット(.outファイル)を表し、$*はターゲットを表しますその拡張子なし。シェルコマンドのすべての$記号は2倍にする必要があり、改行をキャンセルする\を最後に配置しない限り、各コマンドラインは個別のサブシェルで実行されます(したがって、シェルはで始まる1つの長い行を認識します) set -eで終わりdone)。

all: $(patsubst %.in,%.out,$(wildcard foo/*.in))
%.out: %.in
        cp $< $*.tmp.in
        set -e; \
        for f in bar/*; do \
          awk 'NR==FNR{a[$$0]=$$0;next}!a[$$0]' $$f $*.tmp.in >$*.tmp.out; \
          mv $*.tmp.out $*.tmp.in; \
        done
        mv $*.tmp.in $@

これをMakefileというファイルに入れ、make -j12を呼び出します。

GNU Parallel(http://www.gnu.org/software/parallel/)を使用すると、次のようになります。

parallel awk \'NR==FNR\{a\[\$0\]=\$0\;next\}\!a\[\$0\]\' {1} {2} '>{2}.tmp; mv {2}.tmp {2}' ::: bar/* ::: foo/*

これにより、コアごとに1つのジョブが実行されます。使用する -j150%コアごとに1.5ジョブを実行します。

複数のmyScriptを並行して実行したい場合は、次のようにします。

parallel ./myScript ::: foo/*

詳細については、紹介動画をご覧ください: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

2
Ole Tange

ulimitを使用してみてください。 bashのmanページから:

ulimit [-HSTabcdefilmnpqrstuvx [limit]]
Provides control over the resources available to the Shell and to processes started  by  it, 
on systems  that  allow  such control.
[...]
-u     The maximum number of processes available to a single user

だからあなたがulimit -u 8スクリプト内の適切な位置に、そのシェルで使用可能なプロセスを8に制限します。

しかし、それをテストしませんでした。

1
Jari Laamanen