不定期更新雑記

[bash] whileループ内で変数を変更してもループ外に反映されない

Pocket

久々の投稿なので、書き方を忘れています。rickです。お久しぶりです。

唐突のネタですが、昨日(12/19(水))普通にハマって混乱した内容なので、覚え書きも兼ねて書いてみようと思います。

標準出力をwhileで受けて変数に格納する処理

test.txtの3行を順次読み込みながらW_SAMPLE変数に文字列を格納している、ただそれだけの処理です。

私は、当然のように最後のCCCW_SAMPLEに格納されているものと思っていましたが、結果は上記の通りです。

これは有名な話らしいのですが、私は知りませんでした。少しハマって悩んでしまいました。

上記の処理中のプロセスを別コンソールから確認してみました。

分かりにくいですが、プロセスID:5724 が上記の cat | while を実行したbashのプロセスです。

その1つ下に表示されているプロセスID:7730 がパイプを通してforkされたbashのプロセスです。もう別物です。

そしてさらに1つ下に表示されているプロセスID:7731,7777,7818が whileループ内で実行されたsleepコマンドのプロセスです。 whileループの中で3回sleepは実行されていますので、その都度プロセスIDが変わっていますね。

一番最後のpsコマンド結果は、whileループを抜け出したあとなので、コンソール画面に戻ってきている感じですね。

このプロセスの構成だと、そりゃあW_SAMPLECCCは格納されませんよね。

回避方法

上記のような特性を理解した上でシェルスクリプトの実装を進めていけば問題にはならないと思いますが、UNIXのsh(Bourne Shell)では意図した結果になっていたので、移植時に混乱する可能性があります。
(私はHP-UX 11i v3(UNIX)環境下で普通に動作していたシェルスクリプトをRHEL 7.4(Linux)上で動かそうとして捕まりました。可能性じゃなくて混乱しました)

回避策(非互換対応)を知っておいて損はなさそうなのでここに記しておきます。 簡単に言ってしまえば、パイプを使わなければよいのです(多分)。

今回は「標準入力」としてファイルを読み込むように変更しました。 UNIXコマンドは実現方法が多種多様で面白いです。

上記以外にも回避方法はあるかと思います(変数の値をファイルに退避しておく等)。 コーディング/テスト時には気付けると思う罠ですが、他の人が作成したシェルスクリプトを読んだり修正したりする場合は要注意です。

動作確認環境

OS: CentOS Linux release 7.5.1804
bash: GNU bash, バージョン 4.2.46(2)-release (x86_64-redhat-linux-gnu)

, , , , , , ,

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">