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

管道引入了一个子 shell,其中变量的更改从父 shell 中屏蔽掉;这类似于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
% 

不同的子shell,不同的变量。

答案2

管道操作器的两侧平行运行。这是通过在单独的进程中运行两侧来完成的。 (这是在原始 Unix shell 中执行此操作的唯一方法,并且仍然是在现代 shell 中执行此操作的唯一方法。)考虑如下代码片段

testb=left | testb=right
echo $testb

在 zsh 中,管道运算符的右侧在原始进程中运行,左侧在一个子shell。子shell通常是通过一个单独的进程来实现的;即使 shell 优化了执行并且不使用单独的进程,它也会确保对 shell 状态的任何修改(变量、重定向等)都限制在子 shell 中,以保持一致性。因此上面的代码片段会打印出来right

(其他 shell 也可能在子 shell 中运行管道的右侧。在常见 shell 中,只有 zsh 和 ATT ksh 在原始上下文中运行右侧。左侧始终在子 shell 中运行.)

如果您想在不创建子 shell 的情况下通过管道传输数据,可以使用流程替代。进程替换在子 shell 中创建一个管道,并控制原始上下文是连接到管道的输入端还是输出端。

test "testa" > >(cat)

>(请注意和>(…):之间的空格>>(cat)将被解析为追加模式下的进程替换,这会将管道的路径作为第二个参数传递给test。这里我们希望将函数调用的输出重定向到管道。)

相关内容