bashからfishへ乗り換えました 第1回
bash scriptでよくはまる罠を、くどく調べてみます。
この度、長年使い続けていたbashからfishへ乗り換えました。すでに1年使い続けているので、おそらくもうbashに戻ることは無いでしょう。その理由を、bash、fish両方の側面からお話していきたいと思います。

今回はその第1回目。
bashでshell scriptを書き始めた時に誰もがはまる罠
手に馴染むshellは、command line interfaceとして利用するだけでは無く、shell scriptとして利用すれば、日々の業務の効率化に大いに役たちます。
さて、以下のshell script。
実用性は皆無ですが、seqコマンドとpipe(|)を組み合わせて、loop回数をエレガントに(?)制御している気になっています。
行してみると標準出力には、おそらく期待するであろう10では無く、0が出力されます。
$ ./pipe-while.sh
0これは、ちょっとbashに慣れた人がはまる代表的な罠です。
コードを変更して、振る舞いを観測してみましょう。
loopの前後と内側に
ps --forestを挿入してみました。それでは実行してみます。
$ ./pipe-while-2.sh
*** before loop ***
PID TTY TIME CMD
9525 pts/8 00:00:00 bash
10361 pts/8 00:00:00 \_ pipe-while-2.sh
10362 pts/8 00:00:00 \_ ps*** inside loop ***
PID TTY TIME CMD
9525 pts/8 00:00:00 bash
10361 pts/8 00:00:00 \_ pipe-while-2.sh
10364 pts/8 00:00:00 \_ pipe-while-2.sh
10365 pts/8 00:00:00 \_ ps*** after loop ***
PID TTY TIME CMD
9525 pts/8 00:00:00 bash
10361 pts/8 00:00:00 \_ pipe-while-2.sh
10366 pts/8 00:00:00 \_ ps
loopの内側だけpipe-while-2.shから同名の子プロセスが実行され、2つ目のpipe-while-2.shの子プロセスとしてpsコマンドが実行されています。
これからわかることは、
ループの外側と内側が別のプロセス
ということです。
これが、この罠の本質です。
別のプロセスなので、loopの内側で変数の値を変更しても、loopの後に引き継げません。
(loopの外からloopの内側へは、引き継げています。これは子プロセスを生成するときに利用しているシステムコールfork()の仕様です)
この問題をさらに面倒くさくしているのが、
while loopを使えば、もしくはpipeを使えば、必ず同じ事がおきるわけでは無い
ということです。
いくつかサンプルを見てみましょう。
$ ./pipe-only.sh
PID TTY TIME CMD
9525 pts/8 00:00:00 bash
10664 pts/8 00:00:00 \_ pipe-only.sh
10665 pts/8 00:00:00 \_ ps
10666 pts/8 00:00:00 \_ pspipeの前後のpsコマンドが、同じpipe-only.shの子プロセスとして発生しています。
(実行時の状況によりpsプロセスが1つしか表示されない場合があります)
次は pipe を使わずにwhile loopを使ってみます。
$ ./while-only.sh
*** before loop ***
PID TTY TIME CMD
9525 pts/8 00:00:00 bash
10733 pts/8 00:00:00 \_ while-only.sh
10734 pts/8 00:00:00 \_ ps*** inside loop ***
PID TTY TIME CMD
9525 pts/8 00:00:00 bash
10733 pts/8 00:00:00 \_ while-only.sh
10735 pts/8 00:00:00 \_ ps*** after loop ***
PID TTY TIME CMD
9525 pts/8 00:00:00 bash
10733 pts/8 00:00:00 \_ while-only.sh
10737 pts/8 00:00:00 \_ ps
loopの前後、内側のpsコマンドがすべて、while-only.shの子プロセスとして実行されています。
つまり、
pipe と while の合わせ技の時だけ気をつけろ
ということです。
これは、熟練のプログラマも、しばらくbashから離れていると、うっかりやってしまいます。
今回はfishの話まで出来ませんでした(!!??)が、次回は同じ内容についてfishの視点から何か書いてみたいと思います。
それでは皆様よいshell生活を!

