web-dev-qa-db-ja.com

bash:while読み取りループの最後で変数が値を失う

シェルスクリプトの1つに問題があります。何人かの同僚に尋ねたが、彼らは皆、(ひっかいた後)首を振っただけなので、私は答えのためにここに来た。

私の理解によると、次のシェルスクリプトは「Count is 5」を最後の行として出力します。それを除いて。 「カウントは0」と出力します。 「読み取り中」が他の種類のループに置き換えられた場合、問題なく動作します。スクリプトは次のとおりです。

echo "1"> input.data 
 echo "2" >> input.data 
 echo "3" >> input.data 
 echo "4" >> input.data 
 echo "5" >> input.data 
 
 CNT = 0 
 
 cat input.data |読み取り中; 
 do 
 let CNT ++; 
 echo "Counting to $ CNT" 
 done 
 echo "Count is $ CNT" 

なぜこれが起こり、どうすればそれを防ぐことができますか?私はこれをDebian LennyとSqueezeで試しましたが、同じ結果です(つまり、bash 3.2.39とbash 4.1.5。シェルスクリプトウィザードではないことを完全に認めているので、ポインタがあれば幸いです。

38
wolfgangsz

引数@を参照してください Bash FAQエントリ#24: "ループに変数を設定しました。ループが終了した後、なぜ変数が突然消えるのですか?または、なぜデータをパイプできないのですか? read? " (最近アーカイブされた here )。

概要:これは、bash 4.2以降でのみサポートされています。 bashを使用している場合は、パイプの代わりにコマンド置換のようなさまざまな方法を使用する必要があります。

これは一種の「よくある」間違いです。パイプはSubShellを作成するため、while readはスクリプトとは異なるシェルで実行されているため、CNT変数は変更されません(パイプサブシェル内の変数のみ)。

最後のechoをサブシェルwhileとグループ化して修正します(修正する方法は他にもたくさんあります。これは1つの方法です。イアンとイグナシオの回答には他にもあります。)

CNT=0

 cat input.data | ( while read 
do
  let CNT++;
  echo "Counting to $CNT"
done 
echo "Count is $CNT" )

長い説明:

  1. スクリプトでCNTを値0として宣言します。
  2. SubShellは|からwhile readで開始されます。
  3. $CNT変数は、値0でSubShellにエクスポートされます。
  4. SubShellはCNT値をカウントして5に増やします。
  5. SubShellが終了し、変数と値が破棄されます(呼び出しプロセス/スクリプトに戻りません)。
  6. echo元のCNTの値0。
34
coredump

これは機能します

CNT=0 

while read ;
do
  let CNT++;
  echo "Counting to $CNT"
done <input.data
echo "Count is $CNT"
10
user9517

代わりに、whileループの前のファイルのように、サブシェルでデータを渡してみてください。これはlainのソリューションに似ていますが、断続的なファイルが必要ないことを前提としています。

total=0
while read var
do
  echo "variable: $var"
  ((total+=var))
done < <(echo 45) #output from a command, script, or function
echo "total: $total"
6
Steve