10
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

シェルスクリプトで多数のプログラムを同時実行: xargs, nohup, wait

Last updated at Posted at 2018-09-24

サーバーで多数の実験を回すときにスクリプトである程度楽をするためのメモ.

xargs

xargsは, 標準入力から受け取ったものを引数とみなしたコマンドを生成して実行できる.

echo -e "1 2\n4 5\n7 8" | xargs -L 1 -P 2 seq
  • -L 数字: 数字行ずつ読み取って実行する
  • -P 数字: 同時に実行するプロセス数の指定

この例では, echoにより

1 2
4 5
7 8

が1行ずつxargsに入力される.
同時に実行するプロセス数を2と指定しているため, seq 1 2seq 4 5を同時実行したあと, seq 7 8が実行される.

もっと複雑なパラメータを試すには, ()forを囲んでechoするとよい.

(
for n in 10 20 30; do
    for d in `seq 5`; do
        for a in 1 0.1 0.01 0.001; do
            echo $n $d $a
        done
    done
done
) | xargs -L 1 -P 2 ./my_experiment 

xargsのリダイレクト

並列実行するプログラムそれぞれの標準出力をファイルに保存したい場合, 少しややこしくなる.
実行したいコマンドをスクリプトで覆う.

script.sh
#!/bin/sh
seq $1 $2 > seq_$1_$2

作成したスクリプトscript.shをxargsで呼ぶ.

echo -e "1 2\n4 5\n7 8" | xargs -L 1 -P 2 ./script.sh

これにより, seq_1_2, seq_4_5, seq_7_8の3つのファイルそれぞれに結果が書き込まれる.

追記: 1つの引数なら, xargsの-I{}オプション(置換文字列)でいけるみたい.

seq 5 | xargs -L 1 -P 2 -I{} ./my_experiment {}

nohup

並列実行するプログラムそれぞれの標準出力をファイルに保存したい場合, nohupを使う方法もある.

nohupコマンドはバックグラウンドでプログラムを動かすコマンドである.
nohup ./my_experiment 10 1 0.1 > result.txt &
このように打てば, 標準出力はresult.txtに記載され, ログアウトしたあとでも, プログラムは走り続ける. プログラムを終了したいときは,
pkill プロセス名kill プロセスID などを使う. (プロセスに関しては, top, ps, pgrep, lsofなどで確認. )

さて, 本題の並列実行は次のように行う.

#!/bin/sh
for n in 10 20 30; do
    for d in `seq 5`; do
        for a in 1 0.1 0.01 0.001; do
            nohup ./my_experiment $n $d $a > "result${n}_${d}_${a}" &
        done
    done
done

上記の例では, nohupにより, プログラムの終了を待たずに全てのループが回る.
それぞれの引数に対する実験設定に関して, 別々の出力結果を保存できる.

プログラムの終了を同期する: wait

並列数をコントロールするにはwaitコマンドを使う.
wait プロセスID1 プロセスID2 ...
waitコマンドは指定したプロセスが終了するまで, 次の処理に進まない.
そのため, 終了を同期させたいときなどに使える.

#!/bin/bash
for n in 10 20 30; do
    for d in `seq 5`; do
        array=()
        for a in 1 0.1 0.01 0.001; do
            nohup ./my_experiment $n $d $a > "result${n}_${d}_${a}" &
            array+=($!)
        done
        wait ${array[@]}
    done
done

先頭の#!/bin/sh#!/bin/bashに変わっていることに注意. (配列を使用するにはbashにする必要がある. )

$!変数には, 直前のプロセスIDが入るため, array配列は終了を合わせたいプロセスID達が保存されている.

この例では, それぞれのaに関してのみ同時実行され, 4つのaがすべて終わらなければ, 次のdへは進まない.

waitで監視できないプロセスの監視

waitコマンドは, 現在のシェルの子プロセスしか監視できない.

wait 3148
bash: wait: pid 3148 はこのシェルの子プロセスではありません

一度nohupでスクリプトを実行して, ログアウト後, そのスクリプトの終了を監視したいときは, 別の方法を取らなければならない.

#!/bin/bash

while true;do
    pid=`pgrep script1.sh`
    if test -z $pid ; then
        echo "dead"
        nohup ./script2.sh &
        break
    else
        echo "running"
    fi
    sleep 5
done

この例では, script1.shというスクリプトの実行が終わり次第, script2.shを実行する.
監視間隔は5sである.

  • pgrep: プロセス名からプロセスIDを得るコマンド
  • test -z $pid: $pid文字列が空のとき真

まとめ

  • xargs
  • nohupforで同時実行
  • waitで終了同期
  • waitでできない場合, ループで監視
10
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?