如何轻松找出由于 `set -o pipefail` 导致的 bash/zsh 管道失败的部分?

huangapple go评论69阅读模式
英文:

How to easily find out which part of a bash/zsh pipeline failed due to `set -o pipefail`?

问题

I have a bash/zsh command with multiple pipes | that fails when using set -o pipefail. For simplicity assume the command is

set -o pipefail; echo "123456" | head -c2 | grep 5 | cat

如何快速找出第一个失败的命令以及原因?我知道可以检查退出码,但这不显示哪一部分的管道首先失败。

是否有比逐个构建管道并检查第一个失败的退出码更简单的方法?

Edit: 我删除了我编造的代码示例,因为它让人们对我的问题的目的产生了困惑。引发这个问题的实际命令是:

zstdcat metadata.tsv.zst | \
tsv-summarize -H --group-by Nextclade_pango --count | \
tsv-filter -H --ge 'count:2' | \
tsv-select -H -f1 > open_lineages.txt
英文:

I have a bash/zsh command with multiple pipes | that fails when using set -o pipefail. For simplicity assume the command is

set -o pipefail; echo "123456" | head -c2 | grep 5 | cat

How do I quickly find out which command is the first to fail and why? I know I can check the exit code, but that doesn't show which part of the pipeline failed first.

Is there something simpler than the rather verbose check of building up the pipeline one by one and checking for the first failing exit code?

Edit: I removed the contrived code example I made up as it confused people about my purpose of asking. The actual command that prompted this question was:

zstdcat metadata.tsv.zst | \
tsv-summarize -H --group-by Nextclade_pango --count | \
tsv-filter -H --ge 'count:2' | \
tsv-select -H -f1 >open_lineages.txt

答案1

得分: 5

在bash中,在命令之后使用echo "${PIPESTATUS[@]}"来获取每个组件的退出状态,以空格分隔的方式:

#!/bin/bash
$ set -o pipefail; echo "123456" | head -c2 | grep 5 | cat

$ echo ${PIPESTATUS[@]}
0 0 1 0

注意,对于zsh用户,你需要使用小写的pipestatus代替:

#!/bin/zsh
$ set -o pipefail; echo "123456" | head -c2 | grep 5 | cat

$ echo $pipestatus
0 0 1 0

fish中,你也可以简单地使用echo $pipestatus来获得相同的输出。

英文:

In bash, use echo "${PIPESTATUS[@]}" right after the command to get the exit status for each component in a space separated list:

#!/bin/bash
$ set -o pipefail; echo "123456" | head -c2 | grep 5 | cat

$ echo ${PIPESTATUS[@]}
0 0 1 0

Beware zsh users, you need to use the lower case pipestatus instead:

#!/bin/zsh
$ set -o pipefail; echo "123456" | head -c2 | grep 5 | cat

$ echo $pipestatus
0 0 1 0

In fish you can also simply use echo $pipestatus for the same output.

答案2

得分: 0

${PIPESTATUS[@]} right after is the answer you were looking for. However, I want to advise on the first example. It's a good habit to anticipate error, so instead of testing after you should have checked the path prior to everything.

if [ -d "/nonexistent_directory" ]; then
  # here, the pipe shouldn't fail to grep
  # ...unless there's something wrong with "foo"
  # ...but "grep" may fail if the pattern isn't found
  if ! ls -1 "/nonexistent_directory" | grep 'foo' ; then
    echo "The command 'grep foo' failed."
#  else
#    echo "The pipeline succeeded."
  fi
else
  echo "The command 'ls /nonexistent_directory' failed."
fi

Whenever possible, avoid greping ls output in a script; that's fragile...

英文:

${PIPESTATUS[@]} right after is the answer you were looking for. However, I want to advise on the first example. It's a good habit to anticipate error, so instead of testing after you should have check the path prior everything.

if [ -d "/nonexistent_directory" ]; then
  # here pipe shouldn't fail to grep
  # ...unless there's something wrong with "foo"
  # ...but "grep" may be a failure if the pattern isn't found
  if ! ls -1 "/nonexistent_directory" | grep 'foo' ; then
    echo "The command 'grep foo' failed."
#  else
#    echo "The pipeline succeeded."
  fi
else
  echo "The command 'ls /nonexistent_directory' failed."
fi

Whenever possible, avoid greping ls output in script, that' fragile...

huangapple
  • 本文由 发表于 2023年2月9日 00:52:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/75389115.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定