将输出分配给变量时获取管道的退出代码(命令替换)

pow*_*ete 5 bash shell pipe

获取管道命令的退出代码工作正常。

echo "ABC" | false | true
echo ${PIPESTATUS[0]} ${PIPESTATUS[1]} ${PIPESTATUS[2]}
#Output is 0 1 0
Run Code Online (Sandbox Code Playgroud)

但是当我将输出分配给一个变量时,无法获得退出代码。

TEST=$(echo "ABC" | false | true)
echo ${PIPESTATUS[0]} ${PIPESTATUS[1]} ${PIPESTATUS[2]}
#Output is 0
Run Code Online (Sandbox Code Playgroud)

如何获取管道进程的退出代码?

Ada*_*atz 5

Bash 命令替换发生在子 shell 中,因此这些值存在但仅在该子 shell 内。您将看到作业中${PIPESTATUS[0]}的 反映(如果您以 结束替换,则会出现这种情况)。$?1| false

\n\n

我已经更改了您的示例以实际包含一些输出。这也适用于原始代码。

\n\n
# without command substitution\necho "ABC" | false | echo "DEF"\necho "${PIPESTATUS[*]}"\n\necho "---"\n\n# within command substitution\nTEST="$( echo "ABC" | false | echo "DEF"; printf :%s "${PIPESTATUS[*]}" )"\ndeclare -a PIPESTATUS2=( ${TEST##*:} )  # make array w/ content after final colon\nif [[ -n "${TEST%:*}" ]]; then          # if there was original output\n  TEST="${TEST%:*}"                     # remove trailing results from $TEST\n  TEST="${TEST%$\'\\n\'}"                  # remove trailing \\n like plain $(\xe2\x80\xa6)\nelse\n  TEST=""                               # no original output -> empty string\nfi\n\necho "$TEST"\necho "${PIPESTATUS2[*]}"\n
Run Code Online (Sandbox Code Playgroud)\n\n

输出:

\n\n
DEF\n141 1 0\n---\nDEF\n141 1 0\n
Run Code Online (Sandbox Code Playgroud)\n\n

退出代码 141false来自提前终止管道 ( SIGPIPE )的事实。

\n\n

这基本上只是使用冒号作为分隔符将子 shell 的$PIPESTATUS数组附加到存储的值(任何非数字都可以;我选择了一个不需要转义的值),然后将其从响应中取出,用$PIPESTATUS2这些值填充数组。删除最后的换行符是另一种攻击。我们可以使用它作为分隔符,但是如果原始输出没有被换行符终止,那么这就会中断。

\n\n

如果您只想要退出代码之一,则更简单的解决方案

\n\n
TEST=$( echo "ABC" | false | true; exit ${PIPESTATUS[0]} )\necho $?  # 141 from `echo "ABC"\n
Run Code Online (Sandbox Code Playgroud)\n\n

更复杂的 POSIX 兼容解决方案(当您可以使用一些黑魔法时就不需要了):在著名的Csh 编程认为有害的$PIPESTATUS\xc2\xa71d 中搜索“考虑管道”,然后将其适应您的情况符合 POSIX 的需求。如果您是那种需要避免 bash 和真实语言的程序员,那么这将是不平凡的,但非常有教育意义。

\n

  • 写得很好,但我很难将 csh 的批评称为“咆哮”!这是必读的。 (3认同)