![如何在 Bash 脚本中获取 PIPESTATUS 和输出](https://linux22.com/image/1315217/%E5%A6%82%E4%BD%95%E5%9C%A8%20Bash%20%E8%84%9A%E6%9C%AC%E4%B8%AD%E8%8E%B7%E5%8F%96%20PIPESTATUS%20%E5%92%8C%E8%BE%93%E5%87%BA.png)
我正在尝试使用此命令获取文件的最后修改日期
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 pipefail
inbash
获取管道命令序列中最右边的非零退出代码,如$?
。来自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 不可见