有一个简单的测试用例
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
。这里我们希望将函数调用的输出重定向到管道。)