这是一个shell脚本:
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。我有一种奇怪的感觉,管道后面的功能是在一个分叉的shell中调用的......所以,有没有一个简单的解决方法?
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"
答案 0 :(得分:7)
顺便说一句,Korn shell给出了与zsh相同的结果。
请参阅BashFAQ/024。管道在Bash中创建子壳,当子壳退出时变量会丢失。
根据你的例子,我会重组它:
globvar=0
function myfunc {
echo $(($1 + 1))
}
myfunc "$globvar"
globalvar=$(echo "something" | myfunc "$globalvar")
答案 1 :(得分:3)
在myfunc
或sh
中将某些内容放入bash
会导致新的shell生成。您可以通过在myfunc
中添加长睡眠来确认这一点。当它正在睡觉时调用ps,你会看到一个子进程。当函数返回时,该子shell退出而不更改父进程中的值。
如果你真的需要改变这个值,你需要从函数中返回一个值,然后检查$ PIPESTATUS,我猜,就像这样:
globvar=0
function myfunc {
let globvar=globvar+1
echo "myfunc: $globvar"
return $globvar
}
myfunc
echo "something" | myfunc
globvar=${PIPESTATUS[1]}
echo "Global: $globvar"
答案 2 :(得分:0)
问题是'使用内置函数的管道的哪一端由原始进程执行?'
在zsh中,当命令是函数或内置函数时,看起来管道中的最后一个命令是由主shell脚本执行的。
在Bash中(如果你在Linux上,sh可能是Bash的链接),那么两个命令都在子shell中运行,或者第一个命令由主进程运行,其他命令运行通过子壳。
显然,当函数在子shell中运行时,它不会影响父shell中的变量(只有子shell中的全局变量)。
考虑添加额外的测试:
echo Something | { myfunc; echo $globvar; }
echo $globvar
答案 3 :(得分:-1)
使用export
代替let
,否则变量是本地的
(使用$(())也可以进行算术运算)
export globvar=0
function myfunc {
export globvar=$((globvar+1))
echo "myfunc: $globvar"
}