“printf -v”内部功能无法使用重定向输出

时间:2017-10-12 13:14:36

标签: linux bash unix

使用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是否应该将值写入结果变量独立,以确定函数的输出是否通过管道传递到另一个进程?

2 个答案:

答案 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.

这就是为什么函数中的任何变量赋值都不会传递回调用脚本的原因。