组命令{ list; }
应该在当前shell环境中执行list。
这允许在命令组(http://mywiki.wooledge.org/BashGuide/CompoundCommands)之外显示变量赋值等内容。
我用它将输出发送到日志文件以及终端:
{ { echo "Result is 13"; echo "ERROR: division by 0" 1>&2; } | tee -a stdout.txt; } 3>&1 1>&2 2>&3 | tee -a stderr.txt;
关于“将stdout和stderr管道传递给shell脚本中的两个不同进程?”在这里阅读:pipe stdout and stderr to two different processes in shell script?。
{ echo "Result is 13"; echo "ERROR: division by 0" 1>&2; }
模拟输出到stdout和stderr的命令。
我也想评估退出状态。 /bin/true
和/bin/false
模拟可能成功或失败的命令。因此,我尝试将$?
保存到变量r
:
~$ r=init; { /bin/true; r=$?; } | cat; echo $r;
init
~$ r=init; { /bin/true; r=$?; } 2>/dev/null; echo $r;
0
正如您所看到的,上面的管道构造没有设置变量r
,而第二个命令行导致了预期的结果。这是一个错误还是我的错?感谢。
我使用以下版本的bash测试了Ubuntu 12.04.2 LTS(~$
)和Debian GNU / Linux 7.0(wheezy)(~#
):
~$ echo $BASH_VERSION
4.2.25(1)-release
~# echo $BASH_VERSION
4.2.37(1)-release
答案 0 :(得分:0)
我想,你错过/ bin / true returs 0和/ bin / false返回1
$ r ='res:'; {/ bin / true; R + = $ ?; } 2> / dev / null; echo $ r;
RES:0
和
$ r ='res:'; {/ bin / false; R + = $ ?; } 2> / dev / null; echo $ r;
RES:1
答案 1 :(得分:0)
我尝试了一个测试程序:
x=0
{ x=$$ ; echo "$$ $BASHPID $x" ; }
echo $x
x=0
{ x=$$ ; echo "$$ $BASHPID $x" ; } | cat
echo $x
确实 - 看起来管道将先前的代码强制转换到另一个进程,但没有重新初始化bash - 所以$BASHPID
更改但$$
更改。
有关$$
和$BASHPID
之间差异的详细信息,请参阅Difference between bash pid and $$。
同时输出$BASH_SUBSHELL
表示第二位在子shell(级别1)中运行,第一位在级别0。
答案 2 :(得分:0)
bash作为子进程执行管道的所有元素;如果它们是shell内置命令或命令组,则意味着它们在子shell中执行,因此它们设置的任何变量都不会传播到父shell。一般来说这可能很棘手,但如果您只需要命令组的退出状态,则可以使用$ PIPESTATUS数组来获取它:
$ { false; } | cat; echo "${PIPESTATUS[@]}"
1 0
$ { false; } | cat; r=${PIPESTATUS[0]}; echo $r
1
$ { true; } | cat; r=${PIPESTATUS[0]}; echo $r
0
请注意,这仅适用于获取组中 last 命令的退出状态:
$ { false; true; false; uselessvar=$?; } | cat; r=${PIPESTATUS[0]}; echo $r
0
...因为uselessvar=$?
成功了。
答案 3 :(得分:0)
使用变量来保存退出状态不是管道的合适方法:
~$ r=init; { /bin/true; r=$?; } | cat; echo $r; init
管道创建一个子shell。在管道中,退出状态被分配给其值被删除的变量r
的(本地)副本。
所以我想将我的解决方案添加到orginating challenge中,以便在跟踪退出状态的同时将输出发送到日志文件和终端。我决定使用另一个文件描述符。单行格式化可能有点令人困惑......
{ { r=$( { { { echo "Result is 13"; echo "ERROR: division by 0" 1>&2; /bin/false; echo $? 1>&4; } | tee stdout.txt; } 3>&1 1>&2 2>&3 | tee stderr.txt; } 4>&1 1>&2 2>&3 ); } 3>&1; } 1>stdout.term 2>stderr.term; echo r=$r
...所以我申请了一些缩进:
{ { : # no operation r=$( { { { echo "Result is 13" echo "ERROR: division by 0" 1>&2 /bin/false; echo $? 1>&4 } | tee stdout.txt; } 3>&1 1>&2 2>&3 | tee stderr.txt; } 4>&1 1>&2 2>&3 ); } 3>&1; } 1>stdout.term 2>stderr.term; echo r=$r
不介意行"没有操作"。它显示论坛的格式检查器依赖于它,否则会坚持:"您的帖子似乎包含未正确格式化为代码的代码。请使用代码工具栏按钮或CTRL + K键盘快捷键将所有代码缩进4个空格。如需更多编辑帮助,请单击[?]工具栏图标。"
如果执行它会产生以下输出:
r=1
出于演示目的,我将终端输出重定向到文件stdout.term
和stderr.term
。
root@voipterm1:~# cat stdout.txt Result is 13 root@voipterm1:~# cat stderr.txt ERROR: division by 0 root@voipterm1:~# cat stdout.term Result is 13 root@voipterm1:~# cat stderr.term ERROR: division by 0
让我解释一下:
以下group命令模拟一些产生错误代码1的命令以及一些错误消息。文件描述符4在步骤3中声明:
{ echo "Result is 13" echo "ERROR: division by 0" 1>&2 /bin/false; echo $? 1>&4 } | tee stdout.txt;
通过以下代码,使用文件描述符3作为虚拟交换stdout和stderr流。这样就会将错误消息发送到文件stderr.txt
:
{ ... } 3>&1 1>&2 2>&3 | tee stderr.txt;
退出状态已在步骤1中发送到文件描述符4.现在将其重定向到文件描述符1,后者定义变量r的值。错误消息被重定向到文件描述符2,而正常输出("结果为13")附加到文件描述符3:
r=$( { ... } 4>&1 1>&2 2>&3 );
最后文件描述符3被重定向到文件描述符1.这控制输出"结果是13":
{ ... } 3>&1;
最外面的花括号只显示命令的行为。
Gordon Davisson建议利用数组变量PIPESTATUS,其中包含最近执行的前景管道中进程的退出状态值列表。这可能是一种很有前景的方法,但却导致了如何将其价值交给封闭管道的问题。
~# r=init; { { echo "Result is 13"; echo "ERROR: division by 0" 1>&2; } | tee -a stdout.txt; r=${PIPESTATUS[0]}; } 3>&1 1>&2 2>&3 | tee -a stderr.txt; echo "Can you tell me the exit status? $r" ERROR: division by 0 Result is 13 Can you tell me the exit status? init