如果其中一部分失败,如何退出shell脚本?


51

如果其中一部分失败,我如何编写退出的shell脚本?例如,如果以下代码片段失败,则脚本应退出。

n=0
until [ $n -ge 5 ]
do
  gksu *command* && break
  n=$[$n+1]
  sleep 3

Answers:


88

一种方法是将其添加set -e到脚本的开头。这意味着(来自help set):

  -e  Exit immediately if a command exits with a non-zero status.

因此,如果您的任何命令失败,脚本将退出。

或者,您可以exit在可能的故障点添加显式语句:

command || exit 1

5
我(和bash wiki)宁愿人们在正确的错误处理上投入一些思考,而不是使用(由设计IMO破坏)set -e功能。它实际上并不适用于此。5次尝试运行命令失败后,OP希望退出脚本。
斯特凡Chazelas

1
@StéphaneChazelas我不会和你争论它是否坏了,我确定你是对的。但是,OP询问“如果其中一部分失败,我如何编写退出的shell脚本?”,是什么让您认为这是5次失败后退出?
terdon

因为我想不出任何其他方式可以解释这个问题。
斯特凡Chazelas

1
@StéphaneChazelas您可能是正确的。我按字面解释:如果脚本的任何部分失败,整个脚本如何退出。这set -e是我知道的唯一方法。
terdon

在该脚本片段中,可能触发的命令set -esleepbreak作为特殊的内置命令,将导致该脚本在大多数shell中因故障而退出,该脚本中if或左侧的命令&&不受的影响set -en=...如果n为只读则可能会失败,但是就会同时退出该脚本set -e),因此听起来似乎不太可能。我同意这个问题措辞不好。
斯特凡Chazelas

17

您可以在任何位置使用关键字退出脚本exit。您还可以指定退出代码,以向其他程序指示脚本失败或脚本失败的方式,例如exit 1exit 2等。(按照惯例,退出代码0表示成功,大于0表示失败;但是,按照惯例,退出127以上的代码保留用于异常终止(例如,通过信号)。

失败时退出的通用构造是

if [ failure condition ]; then
    exit n
fi

与合适的failure conditionn。但是在特定情况下,您可能会有所不同。现在,对于您的情况,我解释您的问题,即如果五个调用中的任何一个gksu失败,那么您都打算退出。一种方法是使用这样的函数

function try_command {
    for i in 1 2 3 4 5 ; do
        if gksu command ; then
            return 0
        fi
    fi
    exit 1
}

然后,通过调用循环try_command

有(更多)高级或复杂的方法来解决您的问题。但是,比起Stephane的解决方案,初学者更容易使用上述解决方案。


10
attempt=0
until gksu command; do
  attempt=$((attempt + 1))
  if [ "$attempt" -gt 5 ]; then
    exit 1
  fi
done

exit除非在子shell中调用脚本,否则退出脚本。如果脚本的一部分是在子shell,例如因为它是内(...)$(...)或管道线的一部分,那么它只会退出子shell

在这种情况下,如果您希望脚本在子外壳程序之外也退出,那么您需要调用exit该子外壳程序退出。

例如,这里有2个嵌套的子外壳层:

(
  life=hard
  output=$(
    echo blah
    [ "$life" = easy ] || exit 1 # exit subshell
    echo blih not run
  ) || exit # if the subshell exits with a non-zero exit status,
            # exit as well with the same exit status

  echo not run either
) || exit # if the subshell exits with a non-zero exit status,
          # exit as well with the same exit status

如果子外壳是管道的一部分,可能会变得更加棘手。bash有一个特殊的$PIPESTATUS阵列,类似zsh$pipestatus一个可以帮助你在这里:

{
   echo foo
   exit 1
   echo bar
} | wc -c
subshell_ret=${PIPESTATUS[0]}
if [ "$subshell_ret" -ne 0 ]; then
  exit "$subshell_ret"
fi

3

陷阱将在收到信号后执行操作。

trap "echo EXIT;  exit" 0
trap "echo HUP;   exit" 1
trap "echo CTL-C; exit" 2
trap "echo QUIT;  exit" 3
trap "echo ERR;   exit" ERR
n=0
until [ $n -ge 5 ]
do
  n=$[$n+1]
  echo $n
  sleep 3
done

运行它,并使其正常退出。它捕获信号0。

EXIT

再次运行它并用^ C中断。它同时捕获信号2和信号0。

CTL-C
EXIT

非零退出状态将陷入ERR

ERR
EXIT
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.