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

时间:2017-01-18 10:14:05

标签: bash shell pipe

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

echo "ABC" | false | true
echo ${PIPESTATUS[0]} ${PIPESTATUS[1]} ${PIPESTATUS[2]}
#Output is 0 1 0

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

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

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

1 个答案:

答案 0 :(得分:1)

Bash command substitution发生在子shell中,因此这些值只存在于该子shell中。您看到${PIPESTATUS[0]}反映了作业中的$?(如果您使用1结束替换,则会| false。)

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

# without command substitution
echo "ABC" | false | echo "DEF"
echo "${PIPESTATUS[*]}"

echo "---"

# within command substitution
TEST="$( echo "ABC" | false | echo "DEF"; printf :%s "${PIPESTATUS[*]}" )"
declare -a PIPESTATUS2=( ${TEST##*:} )  # make array w/ content after final colon
if [[ -n "${TEST%:*}" ]]; then          # if there was original output
  TEST="${TEST%:*}"                     # remove trailing results from $TEST
  TEST="${TEST%$'\n'}"                  # remove trailing \n like plain $(…)
else
  TEST=""                               # no original output -> empty string
fi

echo "$TEST"
echo "${PIPESTATUS2[*]}"

输出:

DEF
141 1 0
---
DEF
141 1 0

Exit code 141来自false过早终止管道(SIGPIPE)的事实。

这基本上只是将子shell的$PIPESTATUS数组附加到存储的值,使用冒号作为分隔符(任何非数字都可以;我选择了一个我没有必须逃脱),然后将它拉出来在响应之外,使用这些值填充$PIPESTATUS2数组。删除最后的换行符是另一种基础。我们可以使用它作为分隔符,但如果原始输出没有被换行符终止,那么这会破坏。

更简单的解决方案如果您只是想要one of the exit codes

TEST=$( echo "ABC" | false | true; exit ${PIPESTATUS[0]} )
echo $?  # 141 from `echo "ABC"

更复杂的POSIX兼容解决方案(当你可以使用一些黑魔法时,不需要$PIPESTATUS):在着名的{§1d中搜索“考虑管道” {3}}咆哮,然后根据您的POSIX兼容需求进行调整。如果您是那种需要避免使用bash和真实语言的编码器,这将是非常重要但具有高度教育性。