英文:
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...
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论