如何在 Bash 脚本中获取 PIPESTATUS 和输出

如何在 Bash 脚本中获取 PIPESTATUS 和输出

我正在尝试使用此命令获取文件的最后修改日期

TM_LOCAL=`ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'`

执行此行后,TM_LOCAL 的值为“2012-05-16 23:18”

我还想检查 PIPESTATUS 以查看是否有错误。例如,如果文件不存在,则ls返回 2。但$?由于它的返回值为,因此其值为 0 awk

如果我单独运行此命令,我可以通过查看以下代码来检查 ls 的返回值${PIPESTATUS[0]}

ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'

但是$PIPESTATUS,如果我像第一个示例一样将输出分配给变量,则不会像我预期的那样工作。在这种情况下,$PIPESTATUS数组只有 1 个元素,与$?

那么,问题是,我如何才能$PIPESTATUS同时获取两者并将输出分配给变量?

答案1

你可以这样做:

TM_LOCAL=$(ls -l --time-style=long-iso ~/.vimrc | \
             awk '{ print $6" "$7 }' ; exit ${PIPESTATUS[0]} )

然后$?是来自的返回代码ls。如果您需要来自多个管道部分的返回代码,则此方法不起作用(但如果输出不是太大,您可以拆分管道,就像这里一样)。

这是获取完整PIPESTATUS数组和输出的一种相当昂贵的方法。不是很优雅,但我没有找到其他方法:

result=$(echo -e "a\nb\nc" | \
          ( cat ; exit 1 ) | \
          ( cat ; exit 42 ) ; echo ${PIPESTATUS[@]})
output=$(head -n -1 <<< "$result")
status=($(tail -n 1 <<< "$result"))
echo "Output:"
echo "$output"
echo "Results:"
echo "${status[@]}"

这使:

Output:
a
b
c
Results:
0 1 42

答案2

使用set -o pipefailinbash获取管道命令序列中最右边的非零退出代码,如$?。来自man bash

如果设置,管道的返回值是最后一个(最右边)以非零状态退出的命令的值,如果管道中的所有命令都成功退出,则返回值为零。默认情况下禁用此选项。

然后您只需访问 即可$?。使用set +o pipefail再次禁用。

答案3

“您所期望的”的主要问题是,反引号中的命令在子 shell 中执行;$PIPESTATUS存在那里,并且从它返回的状态遵循与执行单个可执行文件(或 shell 脚本)相同的规则。反引号命令的状态是最右边的 ( awk) 状态。

为了实现@丹尼尔·贝克pipefail如上所述,在子 shell 中设置选项如下:

TM_LOCAL=`set -o pipefail; ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'` 现在之后存储的状态将是(如果非零)$?的状态。ls

不过,我认为明确的if [ -f ~/.vimrc ];...测试会更具可读性。

PIPESTATUS如果没有前者的临时文件或将后者编组为字符串,则无法将输出放入变量并返回。

答案4

我只想在退出状态不为零时从 cron 发送电子邮件

诀窍在于,要获取管道末尾的标准输入,您需要将其放在子 shell 中 - 但这似乎隐藏了 PIPESTATUS 值...

测试 cron 输出一些输出并以 1 或 0 退出。

./testcron | (test ${PIPESTATUS[0]} -ne 0 || mail -s "testcron output" paul)

更新:在管道命令处理之前,PIPESTATUS 不可见

相关内容