(1/2)シェルスクリプトプログラミングに取り組むときに最もはまりやすい問題、それが「while問題」だ。今回はその原因を掘り下げてみよう。前回はシェルスクリプトプログラミングのコツの1つとして「アット・ア・グランス性」を紹介した。紹介の中でwhileが引き起こしやすい問題について触れたが、前回の説明だけではよく分からなかった方もいると思う。今回はこの「while問題」に焦点を当て、シェルスクリプトプログラミングで最もはまりやすい問題を掘り下げて説明する。次のシェルスクリプトを見てほしい。実行結果を予測してほしいのだが、おそらくほとんどの方が「標準出力にLinuxが出力される」と答えるだろう。${OS}今度は次のシェルスクリプトの実行結果を想像してみてほしい。ここで「標準出力にLinuxが出力される」と考えた方は、今回の話は最後まで読んだほうが良いということになる。「標準出力にFreeBSDが出力される」と答え、かつ、その理由をシステムコールの動きを取り上げて説明できる方は、今回の話はここで切り上げてもらっても構わない。${OS}実行すると次のようになる。./while02.shFreeBSD$挙動が違うのは、「シェルはパイプが使用されると、コマンドの分だけシェルをfork(2)し、fork(2)して生成されたシェルの方でコマンドを実行する」ためだ。以降でシェルの内部で何が実行されているのか追っていこう。パイプはコマンドの分だけシェルのfork(2)を必要とするtruss(1)を使ってシステムコールの動きを追う。Tukubaiを使用する。追実験する場合にはこのコマンドを使ってみてほしい。Tukubaiのコマンドだ。テキストの整形に便利なのでよく利用する。使い方はオンラインマニュアルにまとまっている。使われているシステムコールの差異だけを取り出すと次のようになる。パイプを挟んだ方はfork(2)システムコールが2回、pipe(2)システムコールが1回、新しく追加されていることが分かる。8パイプを挟んでいるのだから、pipe(2)システムコールが追加されているのは当然だ。問題はfork(2)がなぜ2回呼ばれているのか、という点にある。そこで、次のようなシンプルなモデルを考えてみよう。bシェルはこのようなラインをパースすると、まず自分自身を2回fork(2)する。fork(2)して生成した子プロセスの入出力をpipe(2)で接続する。自分をfork(2)してからbを実行コマンドの入出力をpipe(2)で接続するためには、それぞれを個別のプロセスとして独立させなければならない。このためfork(2)して子プロセスを生成している。パイプが4つ使われていれば、5回fork(2)を実行して子プロセスを生成し、それらをpipe(2)で接続する。パイプが4つ連続して使われていれば、fork(2)は5回実行される。この子プロセスは、シェルではいわゆる「サブシェル」と呼ばれる。メールマガジンをご購読ください。申し込みページへITmediaはアイティメディア株式会社の登録商標です。