サーバーで多数の実験を回すときにスクリプトである程度楽をするためのメモ.
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 2
とseq 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のリダイレクト
並列実行するプログラムそれぞれの標準出力をファイルに保存したい場合, 少しややこしくなる.
実行したいコマンドをスクリプトで覆う.
#!/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
-
nohup
とfor
で同時実行 -
wait
で終了同期 -
wait
でできない場合, ループで監視