如何在bash读取语句中检测失败的子进程?

时间:2017-11-08 05:50:55

标签: bash

在bash中,我们可以使用read和管道到子进程从一系列命令设置环境变量。但是我在一个边缘情况下检测处理中的错误时遇到了问题 - 在错误发生之前,子进程管道的一部分会产生一些输出。

采用输入文件的简化示例,查找以" foo"开头的行。并将var设置为该行的第一个单词:

set -e
set -o pipefail
set -o nounset

die() {
  echo $1 > /dev/stderr
  exit 1
}

read -r var rest < <( \
    cat data.txt \
    | grep foo \
    || die "PIPELINE" \
) || die "OUTER"

echo "var=$var"

使用data.txt

运行此功能
blah
zap foo awesome
bang foo

将输出

var=zap

data.txt文件中运行此文件,该文件不包含foo个输出(到stderr)

DEAD: PIPELINE
DEAD: OUTER

这一切都符合预期。

我们可以在流程结束时引入另一个像cat这样的无操作阶段

...
read -r var rest < <( \
    cat data.txt \
    | grep foo \
    | cat \
    || die "PIPELINE" \
) || die "OUTER"
...

一切都继续有效。

但是如果附加阶段是paste -s -d' '并且输入不包含&#34; foo&#34;输出是

var=
DEAD: PIPELINE

这似乎表明管道错误,但读取成功用空行。 (看起来paste -s -d' '输出一行输出,即使输入为空。)

有没有一种简单的方法来检测管道的这种故障,并导致主脚本出错?

我想我可以检查变量是否为空 - 但这是一个简化版本 - 我实际上使用sedpaste来连接多行来设置多个变量,例如

read -r v1 v2 v3 rest < <( \
   cat data.txt \
   | grep "^foo=" \
   | sed -e 's/foo=//' \
   | paste -s -d' ' \
   || die "PIPELINE"
) || die "OUTER"

2 个答案:

答案 0 :(得分:1)

您可以使用另一个grep来查看paste的输出是否包含某些内容:

read -r var rest < <( \
    cat data.txt \
    | grep foo \
    | paste -s -d' '  \
    | grep . \
    || die "PIPELINE" \
) || die "OUTER"

答案 1 :(得分:0)

最后,我根据具体情况采用了两种不同的解决方案。

第一种方法是将结果传递给临时文件。这将在执行读取之前处理整个文件,因此管道中的任何故障都将导致脚本失败。

cat data.txt \
    | grep "^foo=" \
    | sed -e 's/foo=//' \
    | paste -s -d' ' \
    > $TMP/result.txt
    || die "PIPELINE"

read -r var rest < $TMP/result.txt || die "OUTER"

第二个是测试变量的设置。虽然这意味着 我想避免一堆重复,这似乎是最防弹的解决方案。

read -r var rest < <( cat data.txt \
    | grep "^foo=" \
    | sed -e 's/foo=//' \
    | paste -s -d' ' \
    || die "PIPELINE"
    ) || die "OUTER"

 [ ! -z "$var" ] || die "VARIABLE NOT SET"