このエントリーをはてなブックマークに追加


while内部の変数が反映されない場合の対処

パイプを使って、whileに渡し変数を操作すると、whileを抜けた後で変数を参照しても値が格納されない場合の対処方法です。
原因は、whileは別プロセスとして動作するためwhile内であれば、値は保存されていますが、whileを抜けるとその値は破棄されてしまうためです。


期待した動作をしないwhileスクリプト

#!/bin/bash

# hostsの行をカウントします
i=0
cat /etc/hosts | while read L
do
  i=`expr $i + 1`
  echo inside: $i
done
echo outside: $i

実行結果

$ ./while1.sh
inside: 1
inside: 2
inside: 3
inside: 4
inside: 5
inside: 6
inside: 7
inside: 8
<snip>
outside 0

whileを抜けた後に、$iを出力すると、0になっています。

期待した動作をするスクリプト

パイプを使わず、リダイレクトを使う。

#!/bin/bash

# hostsの行をカウントします
i=0
while read L
do
  i=`expr $i + 1`
  echo inside: $i
done < /etc/hosts
echo outside: $i

実行結果

$ ./while2.sh
inside: 1
inside: 2
inside: 3
inside: 4
inside: 5
inside: 6
inside: 7
inside: 8
<snip>
outside: 43

意図した動作になっています。
しかし、リダイレクトでファイルを指定していますが、stdin(pipe)からの入力の場合はどうするのだ?という問題があります。 解決方法は、以下に記します。

stdin(pipe)による場合の記述方法

Process Substitutionを使うことにより解決します。
man bashをするとProcess Substitutionについて記述されています。

$ LANG=C man bash
  <snip>
  Process Substitution
      Process  substitution is supported on systems that support named pipes (FIFOs) or the /dev/fd method
      of naming open files.  It takes the form of <(list) or >(list).  The process list is  run  with  its
      input  or output connected to a FIFO or some file in /dev/fd.  The name of this file is passed as an
      argument to the current command as the result of the expansion.  If the >(list) form is used,  writ-
      ing  to  the  file  will provide input for list.  If the <(list) form is used, the file passed as an
      argument should be read to obtain the output of list.

      When available, process substitution is performed simultaneously with parameter and variable  expan-
      sion, command substitution, and arithmetic expansion.
<snip>
$ man bash
  <snip>
  プロセス置換
      プロセス置換 (process substitution) がサポートされるのは、 名前付きパイプ (FIFO) または ファイル・ディスクリプターの /dev/fd  形式で
      の指定 をサポートしているシステムです。これは <(list) または >(list) の形になります。 プロセス list は、その入力や出力が FIFO または
      /dev/fd 中の 何らかのファイルに接続された状態で実行されます。 このファイルの名前は、展開の結果として、  引き数の形で現在のコマンドに
      渡されます。 >(list) の形式を使った場合、 ファイルへの書き込みは list への入力となります。 <(list) の形式を使った場合、 引き数として
      渡されたファイルは list の出力を得るために読み込まれます。

      利用可能であれば、プロセス置換 (process substitution) は、 パラメータ展開、変数展開、コマンド置換、算術式展開と同時に行われます。
<snip>

実際にstdinから記述したサンプルスクリプトを以下に記します。
関数にしてみました。

#!/bin/bash

CountLineStdin() {
  if [ ! -p /dev/stdin ]; then
    echo "stdin empty!"
    return 1
  fi
  i=0
  while read line
  do
    i=`expr $i + 1`
    echo "inside: $i"
  done < <(cat -)
  return 0
}
CountLineStdin
echo "outside: $i"

実行結果 $ cat /etc/hosts | ./while3.sh

inside: 1
inside: 2
inside: 3
inside: 4
inside: 5
inside: 6
inside: 7
inside: 8
<snip>
outside: 43

上記出力の通り、期待した動作になっています。

記述に関しての詳細は、man bashおよび、ネット調べてください。
重要なのは、while do ... done の <の記述になります。
done < <(cat -)と記述してください。
スクリプトを簡単に説明すると ! -p /dev/stdin でパイプによる入力があるかを確認しています。
cat -はstdinを出力しています。

以上、while内部の変数が反映されない場合の対処方法でした。



添付ファイル: filewhile1.sh 423件 [詳細] filewhile2.sh 372件 [詳細] filewhile3.sh 439件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2016-11-22 (火) 13:00:56