パイプラインから読み取るwhile読み取りループの後にリセットされる変数
-
26-10-2019 - |
質問
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 read
と while 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
ビルトイン デフォルトで で実行されます 現在 シェルとして使用すると 過去 パイプラインのコマンド。
一見、 ksh
と zsh
デフォルトで 走る どれか のコマンド 過去 のパイプラインのステージ 現在 シェル。
で観察された 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
, 、この場合 - ラインの最初に、そしてそれに従わなければならない文字はないということです。