whileループ内でテストが失敗したときに「ブール」フラグ変数がfalseに保たれない理由を追跡するのに問題があります。
スクリプトにはいくつかのループが含まれていますが、本質的にその目的は、クローンの実行時に毎朝2つのランダムアルバムを再生することです。かつて2つのアルバムを選択する単純な1行のスクリプトがありましたが、再生したくないアルバム(クリスマスミュージックアルバムなど)をブラックリストに追加したかったのです。
そのため、聞きたくないアルバム名を含むwakeUp_blacklist.txtファイルを読み取ることで、一致するものを拒否することができました。ブラックリストに登録されているアルバムをランダムに選択したアルバムと1行ずつ比較して一致するかどうかをテストします。一致するものが見つかったら、その一致を失敗(pass = false
)としてマークし、ループバックして別のアルバムを選択する必要があります。何らかの理由で認知実行がread | while
ループを終了すると、フラグは再び真に切り替わるため、他のアルバムは選択されません。
私のコードとサンプル出力は次のとおりです。
#!/bin/bash
wd="/media/External1/albums"
pass="false"
for ((i=0; i<=1; i++)); do
while [ "$pass" == "false" ]; do
album=`ls -d "$wd"/*/*/ | sort -R | tail -n 1`
echo "album selected:"
echo "$album"
pass="true"
echo "pass reset; pass=$pass"
cat "$wd/wakeUp_blacklist.txt" | while read black; do
echo "pass check 2:$pass"
echo "black: $wd/$black"
if [ "$album" == "$wd/$black" ] || [ "$album\n" == "$albums" ]; then
pass="false"
echo "test failed; pass=$pass"
fi
echo "pass check 1:$pass"
done
echo "Blacklist check complete; pass=$pass" #Why is it true again?!
done
pass="false"
albums="$albums$album\n"
echo "album assigned:"
echo "$album"
done
echo
echo
for album in "$albums"; do
# play "$album*"
echo -e "$album" #List album names rather than actually play them
done
したがって、数回実行すると、最終的にブラックリストにあるアルバムを選択して、次のような出力を提供します。
album selected:
/media/External1/albums/Adele/21/
pass reset; pass=true
pass check 2:true
black: /media/External1/albums/Vince_Guaraldi_Trio/A_Charlie_Brown_Christmas/
pass check 1:true
pass check 2:true
black: /media/External1/albums/Sound_Inventions/Stille_Nacht-_A_German_Christmas/
pass check 1:true
Blacklist check complete; pass=true
album assigned:
/media/External1/albums/Adele/21/
album selected:
/media/External1/albums/Sound_Inventions/Stille_Nacht-_A_German_Christmas/
pass reset; pass=true
pass check 2:true
black: /media/External1/albums/Vince_Guaraldi_Trio/A_Charlie_Brown_Christmas/
pass check 1:true
pass check 2:true
black: /media/External1/albums/Sound_Inventions/Stille_Nacht-_A_German_Christmas/
test failed; pass=false
pass check 1:false
Blacklist check complete; pass=true
album assigned:
/media/External1/albums/Sound_Inventions/Stille_Nacht-_A_German_Christmas/
/media/External1/albums/Adele/21/
/media/External1/albums/Sound_Inventions/Stille_Nacht-_A_German_Christmas/
最後に、ブラックリスト項目が選択したアルバム(German_Christmas)と一致すると、テストは失敗し、変数がありますが、pass
ループfalse
の外側の値の最初の出力を確認すると、再びtrueと表示されることがわかります。pass
read | while
私のスクリプトは十分に効率的ではないかもしれないことを知っていますが、続行する前にここで何が起こっているのか理解したいと思います。どんな提案がありますか?
答え1
あなたは:
cat "$wd/wakeUp_blacklist.txt" | while read black; do
パイプラインの各コマンドは、独自のサブシェルで実行されます。
ㅏサブシェルはbash
独自の状態を持つ別のインスタンスです。これはあなたが右概念的に|
別のインスタンスに「属します」bash
- 最初の既存の値と宣言はコピーされますが、変数は保存場所が異なり、その変更はそのサブシェル内でのみ表示されます(これはうまくいくと思います)。fork
、必要に応じて)。
サブシェルが完了すると、変数のコピーはもう存在しません。 「外部」シェルは変更されていない独自の変数コピーのみを表示できるため、ループの外側で変更が失われるように見えます。
この場合、サブシェルの使用を避けることができますシンプルなリダイレクト:
while read black; do
...
pass="false"
...
done < "$wd/wakeUp_blacklist.txt"
while
ループはデフォルトのシェルで実行され、サブシェルは作成されません。ブラックリストファイルの内容はまだループの標準入力として提供されています。< file
リダイレクト。すべての変数は同じ環境に存在します。このアプローチも役に立たない使用cat
。
一部の他のシェル(特にzsh
)には最初からこの動作がなく、パイプの右側にある変数を自由に変更できます。