グローバル変数が異常に動作するのはwhileループですか、それともパイプラインですか?

グローバル変数が異常に動作するのはwhileループですか、それともパイプラインですか?

COUNTER誰かが(私の観点から)次のコードで変数の奇妙な動作を説明しますか?

#!/bin/bash

COUNTER=0

function increment {
    ((COUNTER++))
}

function report {
    echo "COUNTER: $COUNTER ($1)"
}

function reset_counter {
    COUNTER=0
}

function increment_if_yes {
    answer=$1
    if [ "$answer" == "yes" ]
    then
        increment
    fi
}

function break_it {
    echo -e "maybe\nyes\nno" | \
    while read LL
    do
        increment_if_yes $LL
    done    
}

report start # counter should be 0
increment
report one
increment_if_yes yes
report two
increment_if_yes no
report "still two"

reset_counter
report reset

break_it
report "I'd expect one"

私はそれがスクリプトの終わりにCOUNTERなりたいのですが、それは次のようになります:10

$ ./broken_variable.sh 
COUNTER: 0 (start)
COUNTER: 1 (one)
COUNTER: 2 (two)
COUNTER: 2 (still two)
COUNTER: 0 (reset)
COUNTER: 0 (I'd expect one)

ベストアンサー1

OPの現在のコードはで期待どおりに機能し、ksh他のシェルでも機能できますが、機能しない可能性がありますbash

ループがbreakit()子プロセス内で実行されるようにします。これは、最終的にwhileループのすべての関数呼び出しが子プロセス内でも実行されることを意味します。echo ... | while ...while

子プロセス(whileこの場合はループ)はコピー変数COUNTERなので、子プロセスの変更は次のようにのみCOUNTER適用されます。コピー変わりやすい。子プロセスが終了するとコピー紛失しましたCOUNTER。制御権が親プロセスに返された場合(元)、COUNTER変数は子プロセスを開始する前と同じ値を持ちます。

while目的の動作を達成するには、ループが親プロセスで実行されていることを確認する必要があります。プロセス置換を使用する1つの方法:

while read LL
do
    increment_if_yes "$LL"
done < <( echo -e "maybe\nyes\nno" )

おすすめ記事