2010-11-23 13 views
9

は、ここでシェルスクリプトです:呼び出されたときバッシュ:インクリメントグローバル変数

globvar=0 

function myfunc { 
    let globvar=globvar+1 
    echo "myfunc: $globvar" 
} 

myfunc 
echo "something" | myfunc 

echo "Global: $globvar" 

、それは次のように出力します:

$ sh zzz.sh 
myfunc: 1 
myfunc: 2 
Global: 1 
$ bash zzz.sh 
myfunc: 1 
myfunc: 2 
Global: 1 
$ zsh zzz.sh 
myfunc: 1 
myfunc: 2 
Global: 2 

質問です:なぜこれが起こるとどのような行動が正しいのですか?

P.S.パイプの後ろにある関数が分岐したシェルで呼び出されるという不思議な感覚があります...簡単な回避策がありますか?

P.P.S.この関数は単純なテストラッパーです。テストアプリケーションを実行し、その出力を分析します。 $ PASSEDまたは$ FAILED変数をインクリメントします。最後に、グローバル変数で多数の合格/不合格のテストを取得します。使い方は次のようである:

test-util << EOF | myfunc 
input for test #1 
EOF 
test-util << EOF | myfunc 
input for test #2 
EOF 
echo "Passed: $PASSED, failed: $FAILED" 

おかげで、 セルジュshまたはbashmyfunc

+0

はあなたが読んでいますか? –

+0

よく、私の心に$ globvarはローカル変数ではありません。なぜなら、パイプなしでmyfunc()を呼び出すと、グローバル変数$ globvarがかなり良くインクリメントされるからです。問題は、パイプ越しの関数呼び出しがbash/shでは機能しないことです。 – zserge

答えて

7

Kornシェルは、同じ結果を与えますところで、zshとして。

BashFAQ/024を参照してください。パイプはBashでサブシェルを作成し、サブシェルが終了すると変数は失われます。

あなたの例に基づいて、私はこのような何か、それを再構築します:

globvar=0 

function myfunc { 
    echo $(($1 + 1)) 
} 

myfunc "$globvar" 
globalvar=$(echo "something" | myfunc "$globalvar") 
+0

リンクありがとうございます!問題は、$ PASSEDと$ FAILEDという2つの変数を返す方法です。私が理解できるのは、$を探すことができますか?それがゼロに等しい場合は、自分自身で$ PASSEDを増やします。それ以外の場合は$ FAILEDを増やします。私はちょうどmyfunc()の外でできるだけ小さなコードにしたいと思います。 – zserge

+0

@zserge:これは次のようなものです: 'myfunc(){echo" two words "; }; word1 word2 <<< $(myfunc) 'または' array =($(myfunc)) 'を読み込みます。 –

3

配管何かが新しいシェルを産卵させます。 myfuncに長い睡眠を追加することでこれを確認できます。それは睡眠中にpsとあなたはサブプロセスが表示されます。関数が戻ると、そのサブシェルは親プロセスの値を変更せずに終了します。

あなたが本当に変更する、その値が必要な場合は、関数から値を返す必要があり、このように、私は推測し、後に$ PIPESTATUSをチェックします:

globvar=0 

function myfunc { 
    let globvar=globvar+1 
    echo "myfunc: $globvar" 
    return $globvar 
} 

myfunc 
echo "something" | myfunc 
globvar=${PIPESTATUS[1]} 

echo "Global: $globvar" 
+0

クール。私は前に 'パイパスタ 'について知りませんでした。 – dennycrane

+0

このテクニックの唯一の問題は、戻り値が0-255の範囲内になければならないことです。 –

0

利用export代わりのlet、それ以外の変数はローカル である(算術演算を行うために、同様$を(())を使用します)

export globvar=0 

function myfunc { 
    export globvar=$((globvar+1)) 
    echo "myfunc: $globvar" 
} 
+0

しかし、エクスポートされた変数は呼び出し側のシェルに戻ってこないので、パイプで関数を呼び出すとグローバル変数は変更されません(bashでテストされます) – zserge

+0

私の間違いですが、問題。なぜあなたは単にスクリプトを呼び出すのではなく、新しいシェルを呼び出す必要がありますか? – mb14

+0

実際、私はしません。テストが成功したかどうかを調べるために、テストプログラムの出力をシェル関数にリダイレクトするだけで済みます。出力をリダイレクトする明確な方法はパイプですが、パイプはサブシェルをフォークします。 – zserge

0

問題は、「組み込み関数を使用しているパイプラインの最後が元のプロセスによって実行されるか」です。

zshでは、コマンドが関数または組み込みのときに、メインシェルスクリプトによってパイプラインの最後のコマンドが実行されるように見えます。

Bashでは(Linuxの場合はshがBashへのリンクになる可能性が高い)、どちらのコマンドもサブシェルで実行されるか、最初のコマンドがメインプロセスによって実行され、その他のコマンドはサブシェルによって実行されます。

明らかに、関数がサブシェルで実行されている場合、その関数は親シェルの変数に影響しません(サブシェルのグローバルのみ)。

余分なテストを追加することを検討して:http://tldp.org/LDP/abs/html/localvar.htmlを:

echo Something | { myfunc; echo $globvar; } 
echo $globvar