Home > Archives > 2011年02月16日

2011年02月16日

複数のシェルにコマンドをお届け

今回の論文を書くための実験で,複数のHadoopのクラスタセットひとつひとつに対応するターミナルを開き,それぞれで似たようなコマンドを実行して実験結果を取るという作業をした.コマンドをカットアンドペーストで全部のターミナルに張り付けて実行するとかバカすぎる.なんとかしたい.

という背景で,複数のターミナル(シェル)に対して一箇所から機械的にコマンドを打ち込みたい要求に駆られた.pssh とかだとインタラクティブに出来ないし,そもそも出力のためのターミナルは複数のウインドウにわかれていて欲しい.やりたいのは入力の一本化だけ.

単純に考えると,fifo を使って入力ターミナルからのコマンドを出力ターミナルの標準入力に送ればいい気がする.なので,とりあえず,出力用ターミナルで

cat pipe | bash

としておいて,入力用ターミナルでコマンドを

echo 'cd hoge' > pipe
echo 'ls' > pipe

とかやって送り付けようとした.

が,失敗.何故かコマンドをひとつ発行しただけで出力ターミナルの bash が終わってしまう.これでは複数のコマンドをインタラクティブに発行できず,psshと変わらない.これは望むものではないので原因を探って手法を改善せねばらない.

上の単純な仕組みが失敗した原因は次の通り:最初に送られたコマンドを出力側の bash が読み込んだとき,pipe が EOF に達してしまって "cat pipe | bash" のコマンドが終了してしまう.pipe には後から内容が追加されるけど,とりあえず最初のコマンドの後に一度はファイルの終端まで達するので,シェルのパイプが終了してしまう,と.

さてどうしたものか.

適当に検索してみたら同じようなことを考える人はいたようで,解決策が見つかった."cat pipe | bash" の代わりに次を使えばいい.

tail -f pipe | bash

この tail の -f オプションは,EOFに達しても tail を止めずEOF後に追加された内容も後ろに吐く,という指定.手前のコマンドの出力のプログレスを表示するとかの目的に使われるオプションらしい.これをやっておけばコマンドを間隔を置いて複数送り付けることが出来る.

ということで,前準備@入力ターミナル:

ps="1 2 4"
for p in $ps; do mkfifo pipe-$p; done

前準備@出力ターミナル達:

tail -f pipe-1 | bash
tail -f pipe-2 | bash
tail -f pipe-4 | bash

あとは,適宜全部のターミナルに対して for 文使って少しずつ違うコマンド送ったり,一斉にコマンド発行したり,特定のターミナルにだけ cat 使ってインタラクティブに仕事したり,tee と合わせて特定のサブセットにだけコマンドを送ったり,等など.それなりに使い勝手が良い気がする.

for p in $ps; do echo "procs=$p" > pipe-$p; done
for p in $ps; do echo 'this is pipe-$procs' > pipe-$p; done
for p in $ps; do echo "./runExp.sh" > pipe-$p; done
...
cat | while read line; do echo $line; done > pipe-4
...
cat | while read line; do echo $line; done | tee pipe-2 | tee pipe-4
...
for p in $ps; do echo "cat result-$p.txt" > pipe-$p; done

cat の while read line は1行単位でコマンドを送るため.ここらへんは関数を定義しておいたほうが頭よさげ.

あとは最初のセットアップのところを for 文でいけると楽.ということで,出力ターミナルを一斉に作ってパイプをつなぐコマンド:

for p in $ps; do (echo "(echo 'echo this is pipe-$p'; tail -f pipe-$p) | bash" > pipe-$p &) ;( gnome-terminal -x bash pipe-$p &); done

これで満足.

Home > Archives > 2011年02月16日

Search
Feeds

Page Top