如何在 Shell 命令中捕获 stdout 和 sterr 以及正确的管道退出状态?

如何在 Shell 命令中捕获 stdout 和 sterr 以及正确的管道退出状态?

我希望能够执行以下操作:

command 2>&1 | shell_script.sh "subject line"

运行的 stdout 和 sterrcommand将通过管道传输到其中,shell_script.sh以作为电子邮件正文发送。我想要获得的是退出状态,command 2>&1以便我可以将正确的工作状态附加到电子邮件主题。在当前的实现中,当command 2>&1是通过管道传输时,${PIPESTATUS[0]}始终设置为0。但是,如果我删除2>&1,则 stderr 输出不会通过管道传输到电子邮件正文中。尝试过几种不同的变体,但仍然无法弄清楚。

以下是我的shell_script.sh

#!/usr/bin/env bash
SUBJ="$@"
read EMAIL_BODY
jobsuccess=${PIPESTATUS[0]}

if [ $jobsuccess -eq 0 ]  # Job succeeded
then
        echo $EMAIL_BODY | mail -s "$SUBJ Success" [email protected]
else
        echo $EMAIL_BODY | mail -s "$SUBJ Fail" [email protected]
fi

答案1

#!/bin/bash

subject="subject line"

tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT

if command >"$tmpfile" 2>&1; then
    subject+=" Success"
else
    subject+=" Failure"
fi

mail -s "$subject" [email protected] <"$tmpfile"

因此,将输出保存到临时文件,然后邮寄该文件。发送前根据命令的退出状态设置主题行。不需要两个脚本。

您无法访问PIPESTATUS管道内部,因为管道尚未执行完毕。此外,外部脚本在任何情况下都无法访问它,因为它在自己的环境中运行。

同样不可能的是通过管道传输来自通用命令的输入,然后对该命令的退出状态进行操作。无法从管道本身访问管道中前一个命令的退出状态。

你什么可以do 是将命令包装在输出一段文本的代码中,表示成功或失败。该文本将与其余数据一起通过管道传输(必要时,作为最后的额外信息):

{ if command 2>&1; then echo SUCCESS; else echo FAILURE; } | shell_script.sh

...与shell_script.sh存在

#!/bin/bash

subject="subject line"

tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT

cat >"$tmpfile"

if [[ $(tail -n 1 "$tmpfile") == *SUCCESS* ]]; then
    subject+=" Success"
else
    subject+=" Failure"
fi

sed '$d' "$tmpfile" | mail -s "$subject" [email protected]

这仍然需要将数据保存到临时文件中才能访问它的最后一行(这是使用cat默认情况下读取标准输入来完成的)。然后它决定它是否成功并相应地设置主题。然后,删除最后一行的数据将被邮寄。

另一种选择是运行邮件脚本运行命令(管道中的命令同时执行),然后只需将退出状态作为命令行参数传递即可:

command >command.log 2>&1
shell_script.sh "$?" <command.log
rm -f command.log

脚本看起来像

#!/bin/bash

subject="subject line"

if [[ $1 == 0 ]]; then
    subject+=" Success"
else
    subject+=" Failure"
fi

mail -s "$subject" [email protected]

在这种情况下,要邮寄的数据通过标准输入传递,mail实用程序将默认读取该输入(就像cat前面的示例中所做的那样)。

相关内容