在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' '
输出一行输出,即使输入为空。)
有没有一种简单的方法来检测管道的这种故障,并导致主脚本出错?
我想我可以检查变量是否为空 - 但这是一个简化版本 - 我实际上使用sed
和paste
来连接多行来设置多个变量,例如
read -r v1 v2 v3 rest < <( \
cat data.txt \
| grep "^foo=" \
| sed -e 's/foo=//' \
| paste -s -d' ' \
|| die "PIPELINE"
) || die "OUTER"
答案 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"