对于多个命令,是否有类似于pipefail的东西,比如'try'语句,但在bash中。我想这样做:

echo "trying stuff"
try {
    command1
    command2
    command3
}

在任何时候,如果任何命令失败了,就退出并回显该命令的错误。我不想做这样的事情:

command1
if [ $? -ne 0 ]; then
    echo "command1 borked it"
fi

command2
if [ $? -ne 0 ]; then
    echo "command2 borked it"
fi

等等……或者类似的事情:

pipefail -o
command1 "arg1" "arg2" | command2 "arg1" "arg2" | command3

因为我相信每个命令的参数(如果我错了请纠正我)会相互干扰。这两种方法对我来说似乎非常冗长和讨厌,所以我在这里呼吁一种更有效的方法。


当前回答

另一种方法是简单地将命令与&&连接在一起,这样第一个失败的命令就会阻止其他命令的执行:

command1 &&
  command2 &&
  command3

这不是您在问题中要求的语法,但它是您所描述的用例的通用模式。一般来说,这些命令应该负责打印失败,这样您就不必手动执行了(当您不想要错误时,可以使用-q标志来屏蔽错误)。如果您有修改这些命令的能力,我会将它们编辑为在失败时大喊,而不是将它们包装在具有这种功能的其他东西中。


还要注意,你不需要这样做:

command1
if [ $? -ne 0 ]; then

你可以简单地说:

if ! command1; then

当你确实需要检查返回码时,使用算术上下文而不是[…]- ne:

ret=$?
# do something
if (( ret != 0 )); then

其他回答

run() {
  $*
  if [ $? -ne 0 ]
  then
    echo "$* failed with exit code $?"
    return 1
  else
    return 0
  fi
}

run command1 && run command2 && run command3

假设

alias command1='grep a <<<abc'
alias command2='grep x <<<abc'
alias command3='grep c <<<abc'

要么

{ command1 1>/dev/null || { echo "cmd1 fail"; /bin/false; } } && echo "cmd1 succeed" &&
{ command2 1>/dev/null || { echo "cmd2 fail"; /bin/false; } } && echo "cmd2 succeed" &&
{ command3 1>/dev/null || { echo "cmd3 fail"; /bin/false; } } && echo "cmd3 succeed"

or

{ { command1 1>/dev/null && echo "cmd1 succeed"; } || { echo "cmd1 fail"; /bin/false; } } &&
{ { command2 1>/dev/null && echo "cmd2 succeed"; } || { echo "cmd2 fail"; /bin/false; } } &&
{ { command3 1>/dev/null && echo "cmd3 succeed"; } || { echo "cmd3 fail"; /bin/false; } }

收益率

cmd1 succeed
cmd2 fail

这是乏味的。但是可读性还不错。

不要创建runner函数或使用set -e,而是使用trap:

trap 'echo "error"; do_cleanup failed; exit' ERR
trap 'echo "received signal to stop"; do_cleanup interrupted; exit' SIGQUIT SIGTERM SIGINT

do_cleanup () { rm tempfile; echo "$1 $(date)" >> script_log; }

command1
command2
command3

该trap甚至可以访问触发它的命令的行号和命令行。变量为$BASH_LINENO和$BASH_COMMAND。

就我个人而言,我更喜欢使用轻量级方法,如下所示;

yell() { echo "$0: $*" >&2; }
die() { yell "$*"; exit 111; }
try() { "$@" || die "cannot $*"; }
asuser() { sudo su - "$1" -c "${*:2}"; }

使用示例:

try apt-fast upgrade -y
try asuser vagrant "echo 'uname -a' >> ~/.profile"

另一种方法是简单地将命令与&&连接在一起,这样第一个失败的命令就会阻止其他命令的执行:

command1 &&
  command2 &&
  command3

这不是您在问题中要求的语法,但它是您所描述的用例的通用模式。一般来说,这些命令应该负责打印失败,这样您就不必手动执行了(当您不想要错误时,可以使用-q标志来屏蔽错误)。如果您有修改这些命令的能力,我会将它们编辑为在失败时大喊,而不是将它们包装在具有这种功能的其他东西中。


还要注意,你不需要这样做:

command1
if [ $? -ne 0 ]; then

你可以简单地说:

if ! command1; then

当你确实需要检查返回码时,使用算术上下文而不是[…]- ne:

ret=$?
# do something
if (( ret != 0 )); then