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

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


当前回答

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

run command1 && run command2 && run command3

其他回答

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

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

foo; and echo success; or echo failure

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

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

你说的“退出并回复错误”是什么意思?如果您的意思是希望脚本在任何命令失败时立即终止,那么就这样做

set -e    # DON'T do this.  See commentary below.

在脚本的开头(但注意下面的警告)。不要回显错误消息:让失败的命令处理它。换句话说,如果你这样做:

#!/bin/sh

set -e    # Use caution.  eg, don't do this
command1
command2
command3

而command2失败,同时打印一个错误消息到stderr,那么似乎您已经实现了您想要的。(除非我误解了你想要的!)

因此,您编写的任何命令都必须表现良好:它必须向stderr而不是stdout报告错误(问题中的示例代码将错误输出到stdout),并且在失败时必须以非零状态退出。

However, I no longer consider this to be a good practice. set -e has changed its semantics with different versions of bash, and although it works fine for a simple script, there are so many edge cases that it is essentially unusable. (Consider things like: set -e; foo() { false; echo should not print; } ; foo && echo ok The semantics here are somewhat reasonable, but if you refactor code into a function that relied on the option setting to terminate early, you can easily get bitten.) IMO it is better to write:

 #!/bin/sh

 command1 || exit
 command2 || exit
 command3 || exit

or

#!/bin/sh

command1 && command2 && command3

当我使用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

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

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

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

你可以在非redhat系统上使用@john-kugelman的解决方案,在他的代码中注释掉这一行:

. /etc/init.d/functions

然后,在最后粘贴下面的代码。完全披露:这只是从Centos 7中直接复制和粘贴上述文件的相关位。

在MacOS和Ubuntu 18.04上测试。


BOOTUP=color
RES_COL=60
MOVE_TO_COL="echo -en \\033[${RES_COL}G"
SETCOLOR_SUCCESS="echo -en \\033[1;32m"
SETCOLOR_FAILURE="echo -en \\033[1;31m"
SETCOLOR_WARNING="echo -en \\033[1;33m"
SETCOLOR_NORMAL="echo -en \\033[0;39m"

echo_success() {
    [ "$BOOTUP" = "color" ] && $MOVE_TO_COL
    echo -n "["
    [ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS
    echo -n $"  OK  "
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo -n "]"
    echo -ne "\r"
    return 0
}

echo_failure() {
    [ "$BOOTUP" = "color" ] && $MOVE_TO_COL
    echo -n "["
    [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
    echo -n $"FAILED"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo -n "]"
    echo -ne "\r"
    return 1
}

echo_passed() {
    [ "$BOOTUP" = "color" ] && $MOVE_TO_COL
    echo -n "["
    [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
    echo -n $"PASSED"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo -n "]"
    echo -ne "\r"
    return 1
}

echo_warning() {
    [ "$BOOTUP" = "color" ] && $MOVE_TO_COL
    echo -n "["
    [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
    echo -n $"WARNING"
    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
    echo -n "]"
    echo -ne "\r"
    return 1
}