パイプラインから読み取るwhile読み取りループの後にリセットされる変数

StackOverflow https://stackoverflow.com/questions/7313491

質問

initiate () {
read -p "Location(s) to look for .bsp files in? " loc
find $loc -name "*.bsp" | while read
do
    if [ -f "$loc.bz2" ]
    then
        continue
    else
        filcount=$[$filcount+1]
        bzip $loc
    fi
    if [ "$scan" == "1" ]; then bzipint $loc
    fi
    echo $filcount    #Correct counting
    echo $zipcount    #Correct counting
    echo $scacount    #Correct counting
    echo $valid       #Equal to 1
done

echo $filcount    #Reset to 0
echo $zipcount    #Reset to 0
echo $scacount    #Reset to 0
echo $valid       #Still equal to 1
}

使用するバッシュシェルスクリプトを書いています bzip2 すべてをジップアップします .bsp ディレクトリ内のファイル。このスクリプトでは、合計をカウントするためのいくつかの変数(ファイル、ZIPの成功、整合性スキャンの成功)がありますが、問題に遭遇したようです。

いつ find $loc -name "*.bsp" ファイルを使い果たして提供します while readwhile read 出口、それはゼロアウトします $filcount, $zipcount$scacount (そのすべてが内部で変更(増加) initiate (), bzip () (これは中に呼び出されます initiate ()) また bzipint () (これはも呼ばれます initiate ()).

内部が変化する変数と関係があるかどうかをテストするために initiate () またはそれからアクセスした他の機能、私はエコーを使用しました $valid, 、以外で定義されています initiate () (お気に入り $filcount, $zipcount, 、など)、しかし内部の別の関数から変更されません initiate () または内部 initiate () 自体。

大変興味深いことに、 $valid Initiate内の他の変数のように、0にリセットされません。

読み取り中に私の変数が魔法のようにリセットされる理由を教えてもらえますか?

役に立ちましたか?

解決

昨日この問題に遭遇しました。

問題はあなたがしていることです find $loc -name "*.bsp" | while read. 。これにはパイプが含まれるため、 while read ループは、実際には、スクリプトの残りの部分と同じバッシュプロセスで実行できません。 bashは、サブプロセスを生み出して、のstdoutを接続できるようにしなければなりません。 find のstdinに while ループ。

これはすべて非常に賢いですが、ループに設定された変数はループの後には見えないことを意味します。 while ループ私は書いていました。

パイプを使用せずにループに入力を送信しようとするか、変数を使用せずにループから出力を取得することができます。私は一時的なファイルに書き込み、ループ全体を包むことの両方を含む恐ろしい憎しみになりました $(...), 、 そのようです:

var="$(producer | while read line; do
    ...
    echo "${something}"
done)"

ループからエコーされたすべてのものにvarが設定されました。私はおそらくその例の構文を台無しにしたでしょう。私は今書いたコードを持っていません。

他のヒント

Bashを使用する場合

while read
do
    if [ -f "$REPLY.bz2" ]
    then
        continue
    else
        filcount=$[$filcount+1]
        bzip $REPLY
    fi
    if [ "$scan" == "1" ]; then bzipint $REPLY
    fi
    echo $filcount    #Correct counting
    echo $zipcount    #Correct counting
    echo $scacount    #Correct counting
    echo $valid       #Equal to 1
done < <(find $loc -name "*.bsp")

オプションを要約します 為に 使用 read 概念に相当する]パイプラインの終わりに ポジックスのようなシェル:

要約する:デフォルトでbashで、そして常にposixに準拠したシェルで、常に、 パイプラインのすべてのコマンドはaで実行されます サブシェル, 、 それで 彼らが作成または変更する変数は、 現在 シェル (パイプラインが終了した後は存在しません)。

以下 カバー bash, ksh, zsh, 、 と sh ([主に] Posix-Featuresのみのシェルなど dash)そして示しています 作成 /変更された変数を保存するために、サブシェルの作成を回避する方法 read.

最小バージョン番号が指定されていない場合は、「かなり古い」バージョンでさえそれをサポートしていると仮定します(問題の機能は長い間存在していますが、紹介されたときは特にわかりません。

以下のソリューションに代わる[posix準拠]の代替として注意してください 一時]ファイルでコマンドの出力をいつでもキャプチャしてから、 read なので < file, 、サブシェルも回避します。


ksh, 、 と zsh: :回避策/構成の変更はまったく必要ありません:

read ビルトイン デフォルトで で実行されます 現在 シェルとして使用すると 過去 パイプラインのコマンド。

一見、 kshzsh デフォルトで 走る どれか のコマンド 過去 のパイプラインのステージ 現在 シェル。
で観察された ksh 93u+zsh 5.0.5.
この機能が導入されたバージョンで具体的に知っている場合は、お知らせください。

#!/usr/bin/env ksh
#!/usr/bin/env zsh

out= # initialize output variable

# Pipe multiple lines to the `while` loop and collect the values in the output variable.
printf '%s\n' one two three | 
 while read -r var; do
   out+="$var/"
 done

echo "$out" # -> 'one/two/three/'

bash 4.2+: : 使用 lastpipe シェルオプション

Bashバージョン4.2以上で、シェルオプションをオンにする lastpipe 最後のパイプラインセグメントが実行されます 現在 シェル、読み取りが現在のシェルに見える変数を作成できるようにします。

#!/usr/bin/env bash

shopt -s lastpipe # bash 4.2+: make the last pipeline command run in *current* shell

out=
printf '%s\n' one two three | 
 while read -r var; do
   out+="$var/"
 done

echo "$out" # -> 'one/two/three/'

bash, ksh, zsh: : 使用する プロセス代替

大まかに言えば、プロセス代替は、一時的なファイルのようにコマンドの出力を行う方法です。

out=
while read -r var; do
  out+="$var/"
done < <(printf '%s\n' one two three) # <(...) is the process substitution

echo "$out" # -> 'one/two/three'

bash, ksh, zsh: : 使う ここによる とともに コマンド代替

out=
while read -r var; do
  out+="$var/"
done <<< "$(printf '%s\n' one two three)" # <<< is the here-string operator

echo "$out" # -> 'one/two/three'

コマンドの代替を二重引用して、その出力を保護する必要性に注意してください シェル拡張.


Posix準拠のソリューション(sh): 使う ここにドキュメント とともに コマンド代替

#!/bin/sh

out=
while read -r var; do
  out="$out$var/"
done <<EOF # <<EOF ... EOF is the here-doc
$(printf '%s\n' one two three)
EOF

echo "$out" # -> 'one/two/three'

デフォルトでは、エンディングデリミターを配置する必要があることに注意してください - EOF, 、この場合 - ラインの最初に、そしてそれに従わなければならない文字はないということです。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top