パイプライン機能でzshの割り当てが失敗します。

パイプライン機能でzshの割り当てが失敗します。

簡単なテストケースがあります

local testa=("a")
local testb=("b")

function test { testb=(${(P)1}) }

test "testa"
echo "testb="$testb

出力testb=a

local testa=("a")
local testb=("b")

function test { testb=(${(P)1}) }

test "testa" | cat
echo "testb="$testb

出力は次のとおりですtestb=b

私は同じ結果を出力に適用する必要があると仮定しますtestb=a

何が問題なの?

答え1

パイプは、変数の変更が親シェルから隠されるサブシェルを導入します。whileループ内の変数の変更が親項目に影響しないパイプを含むループ。モジュールを介してPIDの変更を観察できますzsh/system

zmodload zsh/system

local testa=("a")
local testb=("b")

function test {
    testb=(${(P)1})
    echo "INSIDE testb=$testb $sysparams[pid]"
}

test "testa" | cat
echo "OUTSIDE testb=$testb $sysparams[pid]"

次のように表示する必要があります

% zsh foo.zsh
INSIDE testb=a 61488
OUTSIDE testb=b 61487
% 

異なるサブシェル、異なる変数。

答え2

パイプオペレータの両側が平行に移動します。これは、別々のプロセスで両方を実行することによって行われます。 (これは元のUnixシェルでこれを行う唯一の方法であり、現代のシェルでもこれを行う唯一の方法です。)次のコードスニペットを考えてみましょう。

testb=left | testb=right
echo $testb

zshでは、パイプ演算子の右側は元のプロセスで実行され、左側は次のように実行されます。サブシェル。サブシェルは通常、別々のプロセスによって実装されます。シェルが実行を最適化し、別のプロセスを使用しなくても、シェルの状態(変数、リダイレクトなど)に対するすべての変更がサブシェルに制限されるため、一貫性が維持されます。 。したがって、上記のコードが印刷されますright

(他のシェルもサブシェルでパイプの右側を実行できます。通常のシェルのうち、zshとATT kshのみが元のコンテキストで右側を実行します。左側は常にサブシェルで実行されます。)

サブシェルを作成せずにデータをパイプ処理するには、次のものを使用できます。プロセスの交換。プロセス置換はサブシェルにパイプを作成し、元のコンテキストがパイプの入力または出力に接続されるかどうかを制御します。

test "testa" > >(cat)

>>(…)と:の間のスペースは追加モード>>(cat)でプロセス全体で解析され、パイプパスを2番目の引数として渡します。testここでは、関数呼び出しの出力をパイプにリダイレクトしようとしています)

関連情報