如何在bash脚本中同时获取PIPESTATUS和输出


9

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

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

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

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

如果仅运行此命令,则可以通过查看以下内容检查ls的返回值: ${PIPESTATUS[0]}

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

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

因此,问题是,如何同时获得两者$PIPESTATUS并将输出分配给变量?

Answers:


8

您可以这样做:

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

就我而言,这项工作有效,但我仍然对是否有办法获取完整的pipestatus数组和输出感到好奇。
Mustafa SerdarŞanlı2012年

3

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

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

然后,您可以简单地访问$?。使用set +o pipefail再次禁用。


2

我认为这里的问题是,执行命令后,PIPESTATUS会完全消失。您可以通过bash版本2或更高版本获取完整的PIPESTATUS数组,方法如下:

declare -a status
status=(${PIPESTATUS[@]})

然后访问${status[0]}${status[1]}等等。


2

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

要实现@ Daniel Beck所说的,请pipefail在子shell中设置选项:

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

但是,我认为显式if [ -f ~/.vimrc ];测试更易读。

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


0

我只想在退出状态不为零的情况下发送电子邮件到cron

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

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

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

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


0

一种选择是在调用之前获取文件的修改时间,然后检查文件是否存在stat。由于 stat返回的时间戳比您在时间戳中的要多,因此可以使用参数扩展对其进行修整。

使用GNU stat(例如在Linux上),您可以运行:

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -c '%y' ~/.vimrc 2>/dev/null)
TM_LOCAL=${TM_LOCAL%:*}  # Safe to do, even if stat fails

在Mac OS X和其他BSD系统上,stat语法不同,可以指定时间格式:

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -f '%Sm' -t '%Y-%m-%d %H:%M' ~/.vimrc 2>/dev/null)

在现在的GNU答复中,您说更改$TM_LOCAL为安全的。只有当您期望它没有先验价值时,它才是安全的。说该值是以前的值,2020-02-27 17:14并且没有~/.vimrc文件。然后,您将拥有2020-02-27 17。因此,我将这两行与其他行链接在一起,&&或者(最好是因为它不太容易理解)使用一个if节。
亚当·卡兹
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.