对于多个命令,是否有类似于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

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


当前回答

对于无意中发现这条线索的鱼壳用户。

假设foo是一个不“返回”(回显)值的函数,但它像往常一样设置退出码。 为了避免在调用函数后检查$status,你可以这样做:

foo; and echo success; or echo failure

如果它太长,不能放在一行上:

foo; and begin
  echo success
end; or begin
  echo failure
end

其他回答

当我使用ssh时,我需要在errexit (set -e)模式下区分由连接问题和远程命令的错误代码引起的问题。我使用以下函数:

# prepare environment on calling site:

rssh="ssh -o ConnectionTimeout=5 -l root $remote_ip"

function exit255 {
    local flags=$-
    set +e
    "$@"
    local status=$?
    set -$flags
    if [[ $status == 255 ]]
    then
        exit 255
    else
        return $status
    fi
}
export -f exit255

# callee:

set -e
set -o pipefail

[[ $rssh ]]
[[ $remote_ip ]]
[[ $( type -t exit255 ) == "function" ]]

rjournaldir="/var/log/journal"
if exit255 $rssh "[[ ! -d '$rjournaldir/' ]]"
then
    $rssh "mkdir '$rjournaldir/'"
fi
rconf="/etc/systemd/journald.conf"
if [[ $( $rssh "grep '#Storage=auto' '$rconf'" ) ]]
then
    $rssh "sed -i 's/#Storage=auto/Storage=persistent/' '$rconf'"
fi
$rssh systemctl reenable systemd-journald.service
$rssh systemctl is-enabled systemd-journald.service
$rssh systemctl restart systemd-journald.service
sleep 1
$rssh systemctl status systemd-journald.service
$rssh systemctl is-active systemd-journald.service

不要创建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。

以功能的方式检查状态

assert_exit_status() {

  lambda() {
    local val_fd=$(echo $@ | tr -d ' ' | cut -d':' -f2)
    local arg=$1
    shift
    shift
    local cmd=$(echo $@ | xargs -E ':')
    local val=$(cat $val_fd)
    eval $arg=$val
    eval $cmd
  }

  local lambda=$1
  shift

  eval $@
  local ret=$?
  $lambda : <(echo $ret)

}

用法:

assert_exit_status 'lambda status -> [[ $status -ne 0 ]] && echo Status is $status.' lls

输出

Status is 127

不管怎样,编写代码来检查每个命令是否成功的更简单的方法是:

command1 || echo "command1 borked it"
command2 || echo "command2 borked it"

它仍然很乏味,但至少是可读的。

假设

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

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