Bash:変数の割り当てが「固定」されていないようです。

Bash:変数の割り当てが「固定」されていないようです。

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と表示されることがわかります。passread | 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)には最初からこの動作がなく、パイプの右側にある変数を自由に変更できます。

関連情報