使用bash 4.1.2和4.3.48,以下脚本给出了预期的输出:
#!/bin/bash
returnSimple() {
local __resultvar=$1
printf -v "$__resultvar" '%s' "ERROR"
echo "Hello World"
}
returnSimple theResult
echo ${theResult}
echo Done.
按预期输出:
$ ./returnSimple
Hello World
ERROR
Done.
但是,当函数的stdout通过管道传递到另一个进程时,__resultvar
变量的赋值不再起作用了:
#!/bin/bash
returnSimple() {
local __resultvar=$1
printf -v "$__resultvar" '%s' "ERROR"
echo "Hello World"
}
returnSimple theResult | cat
echo ${theResult}
echo Done.
意外输出:
$ ./returnSimple
Hello World
Done.
为什么printf -v
在第二种情况下不起作用? printf -v
是否应该将值写入结果变量独立,以确定函数的输出是否通过管道传递到另一个进程?
答案 0 :(得分:8)
请参阅man bash
上的Pipelines
部分:
管道中的每个命令都作为一个单独的进程执行(即在子shell中)。
这就是为什么当你写cmd | cat
时,cmd
会收到一个无法修改的变量副本。
一个简单的演示:
$ test() ((a++))
$ echo $a
$ test
$ echo $a
1
$ test | cat
$ echo $a
1
答案 1 :(得分:2)
有趣的是,使用eval $__resultvar="'ERROR'"
而不是printf -v
语句时也会发生同样的情况。因此,这不是printf
相关问题。
相反,在主脚本和函数中添加echo $BASH_SUBSHELL
表明shell在第二种情况下生成子shell - 因为它需要将函数的输出传递给另一个进程。因此该函数在子shell中运行:
#!/bin/bash
returnSimple() {
local __resultvar=$1
echo "Sub shell level: $BASH_SUBSHELL"
printf -v "$__resultvar" '%s' "ERROR"
}
echo "Sub shell level: $BASH_SUBSHELL"
returnSimple theResult | cat
echo ${theResult}
echo Done.
输出:
% ./returnSimple.sh
Sub shell level: 0
Sub shell level: 1
Done.
这就是为什么函数中的任何变量赋值都不会传递回调用脚本的原因。