颜林林的个人网站

管道与命令返回值

2024-08-04 00:14
题图
(题图由AI生成)

在编写流程时,我们经常会使用管道,将多个命令串到一起来执行。前一个命令的输出作为后一个命令的输入,省去中间文件的读写,这是一个很有效率的做法。例如:

1
cat input.txt | grep "pattern" | sort > output.txt

在这个例子中,cat 命令读取 input.txt 文件的内容,grep 命令筛选包含 “pattern” 的行,最后 sort 命令对筛选结果进行排序并输出到 output.txt 文件。

然而,需要注意的是,如果这些命令中出现错误,导致中途失败退出,我们应该如何进行判断和终止流程,才能确保逻辑正确,且运行符合预期。

判断管道中命令的执行状态

判断一个流程是否失败,我们通常使用 $? 来判断,例如:

1
2
3
4
5
6
command-1
if [ $? -ne 0 ]; then
  echo "command-1 failed"
  exit 1
fi
command-2

如果多个命令,例如:

1
command-1 | command-2 | command-3

如果 command-2 失败,而 command-3 成功,则 $? 给出的信息是不正确的,这可能导致错判。因为 $? 只返回最后一个命令 command-3 的退出状态码,而不是整个管道中所有命令的状态。

使用 PIPESTATUS 数组获取每个命令的状态码

Bash 提供了一个名为 PIPESTATUS 的数组变量,它包含管道中每个命令的退出状态码。通过使用 PIPESTATUS,我们可以准确地判断管道中每个命令的执行情况。

举例:

1
2
echo "Hello World" | grep "Hello" | wc -l
echo "\$?: $?,  PIPESTATUS: ${PIPESTATUS[*]}"

输出可能是:

1
$?: 0,  PIPESTATUS: 0 0 0

这个输出表示三个命令都成功执行,并返回值为 0

再举例:

1
2
echo "Hello World" | grep "Bye" | wc -l
echo "\$?: $?,  PIPESTATUS: ${PIPESTATUS[*]}"

输出可能是:

1
$?: 0,  PIPESTATUS: 0 1 0

这个输出表示 grep "Bye" 命令失败(返回值为 1),而其他命令成功(返回值为 0)。

总结

在使用管道时,简单依赖 $? 来判断流程是否正确是不够的,因为它只返回最后一个命令的退出状态码。使用 PIPESTATUS 数组可以帮助我们更准确地进行判断,确保每个命令都执行成功。如果 PIPESTATUS 数组中的任何一个值不为 0,则表明对应的命令执行失败,可以根据这些信息来进行错误处理和流程控制。

通过这种方式,我们可以确保我们的流程在遇到错误时能够正确地进行判断和终止,保证逻辑的正确性和运行的符合预期。

--- END ---

注:本文同时发表于“不靠谱颜论”公众号

相关文章