我想在Bash中执行一个长时间运行的命令,并捕获它的退出状态,并输出它的输出。
所以我这样做:
command | tee out.txt
ST=$?
问题是变量ST捕获tee的退出状态,而不是命令的退出状态。我怎么解决这个问题?
注意,该命令长时间运行,将输出重定向到文件以供以后查看,这对我来说不是一个好的解决方案。
我想在Bash中执行一个长时间运行的命令,并捕获它的退出状态,并输出它的输出。
所以我这样做:
command | tee out.txt
ST=$?
问题是变量ST捕获tee的退出状态,而不是命令的退出状态。我怎么解决这个问题?
注意,该命令长时间运行,将输出重定向到文件以供以后查看,这对我来说不是一个好的解决方案。
当前回答
PIPESTATUS[@]必须在管道命令返回后立即复制到一个数组中。 任何对PIPESTATUS[@]的读取都将删除该内容。 如果计划检查所有管道命令的状态,则将其复制到另一个数组。 $?与${PIPESTATUS[@]}的最后一个元素值相同, 读它似乎破坏了“${PIPESTATUS[@]}”,但我还没有完全验证这一点。
declare -a PSA
cmd1 | cmd2 | cmd3
PSA=( "${PIPESTATUS[@]}" )
如果管道位于子外壳中,这将不起作用。为了解决这个问题, 查看bash pipestatus在backtick命令?
其他回答
有一个内部Bash变量叫做$PIPESTATUS;它是一个数组,保存最后一个前台命令管道中每个命令的退出状态。
<command> | tee out.txt ; test ${PIPESTATUS[0]} -eq 0
或者另一种也适用于其他shell(如zsh)的选择是启用pipefail:
set -o pipefail
...
由于语法略有不同,第一个选项不能用于zsh。
使用外部命令有时可能比深入研究bash的细节更简单、更清晰。管道,来自最小的进程脚本语言execline,以第二个命令*的返回代码退出,就像sh管道一样,但与sh不同的是,它允许反转管道的方向,以便我们可以捕获生产者进程的返回代码(以下都是在sh命令行上,但安装了execline):
$ # using the full execline grammar with the execlineb parser:
$ execlineb -c 'pipeline { echo "hello world" } tee out.txt'
hello world
$ cat out.txt
hello world
$ # for these simple examples, one can forego the parser and just use "" as a separator
$ # traditional order
$ pipeline echo "hello world" "" tee out.txt
hello world
$ # "write" order (second command writes rather than reads)
$ pipeline -w tee out.txt "" echo "hello world"
hello world
$ # pipeline execs into the second command, so that's the RC we get
$ pipeline -w tee out.txt "" false; echo $?
1
$ pipeline -w tee out.txt "" true; echo $?
0
$ # output and exit status
$ pipeline -w tee out.txt "" sh -c "echo 'hello world'; exit 42"; echo "RC: $?"
hello world
RC: 42
$ cat out.txt
hello world
使用管道与原生bash管道的区别与答案#43972501中使用的bash进程替换相同。
*实际上管道不退出,除非有一个错误。它执行到第二个命令中,所以是第二个命令执行返回。
为什么不使用stderr?像这样:
(
# Our long-running process that exits abnormally
( for i in {1..100} ; do echo ploop ; sleep 0.5 ; done ; exit 5 )
echo $? 1>&2 # We pass the exit status of our long-running process to stderr (fd 2).
) | tee ploop.out
所以ploop。Out接收标准输出。Stderr接收长时间运行的进程的退出状态。这样做的好处是完全与posix兼容。
(好吧,除了示例长时间运行的进程中的range表达式,但这并不真正相关。)
这是它的样子:
...
ploop
ploop
ploop
ploop
ploop
ploop
ploop
ploop
ploop
ploop
5
请注意,返回代码5不会输出到文件plop .out。
(command | tee out.txt; exit ${PIPESTATUS[0]})
与@cODAR的回答不同,它返回第一个命令的原始退出码,不仅成功为0,失败为127。但是正如@Chaoran指出的,你可以直接调用${PIPESTATUS[0]}。但重要的是,所有的都放在括号里。
有一个数组为您提供管道中每个命令的退出状态。
$ cat x| sed 's///'
cat: x: No such file or directory
$ echo $?
0
$ cat x| sed 's///'
cat: x: No such file or directory
$ echo ${PIPESTATUS[*]}
1 0
$ touch x
$ cat x| sed 's'
sed: 1: "s": substitute pattern can not be delimited by newline or backslash
$ echo ${PIPESTATUS[*]}
0 1