首页 > 解决方案 > Bash:设置“set -e”时捕获退出代码而不是中止?

问题描述

在大多数情况下,我更喜欢我的脚本在遇到意外错误时终止,因此通常以set -e -u -E -o pipefail.

但是,有时我需要捕获单个调用的退出代码而不是终止。这导致了类似的结构

# (1) Verbose. Breaks when moved between scripts 
#              with different global -e setting.
set +e
COMMAND WITH A LOT OF ARGUMENTS \
    | AND MAYBE EVEN PIPES \
    | MAYBE OVER MANY LINES
exitCode=$?
set -e

# (2) Verbose.
if COMMAND WITH A LOT OF ARGUMENTS \
    | AND MAYBE EVEN PIPES \
    | MAYBE OVER MANY LINES
then exitCode=$?
else exitCode=$?
fi

# (3)
exitCode=0
COMMAND WITH A LOT OF ARGUMENTS \
    | AND MAYBE EVEN PIPES \
    | MAYBE OVER MANY LINES \
    || exitCode=$?

# (4) Brittle, must not mix up order of && and ||
COMMAND WITH A LOT OF ARGUMENTS \
    | AND MAYBE EVEN PIPES \
    | MAYBE OVER MANY LINES \
     && exitCode=$? || exitCode=$?

# (5), doesn't work with `set -o pipefail`
COMMAND WITH A LOT OF ARGUMENTS \
    | AND MAYBE EVEN PIPES \
    | MAYBE OVER MANY LINES | cat
exitCode=$PIPESTATUS

# (6) Requires maintaining a function across scripts.
captureExitCode COMMAND WITH A LOT OF ARGUMENTS \
    | AND MAYBE EVEN PIPES \
    | MAYBE OVER MANY LINES
# where
captureExitCode() {
    # any of the above constructs with "$@" as command
}

所有这些要么很冗长,要么在视觉上有些难以解析,尤其COMMAND ...是在多行管道命令的情况下。至少,他们没有很好地传达他们的意图。在函数的情况下,captureExitCode必须跨脚本复制。

是否有一些不那么冗长的内置习语来捕获退出代码?

标签: bash

解决方案


在子 shell 中运行有问题的命令并禁用errexit

(
  set +e
  run buggy code failing without proper error handling
)
exitcode=$?

例子:

#! /bin/bash

set -e

if ( set +e
     false
   )
then
  echo success
else
  echo failure
fi

顺便说一句:当您以这种方式编写管道时,您不需要反引号:

echo abc |
  tr a x |
  sed 's/^x/y/'

推荐阅读