Bash中的错误处理

时间:2020-03-05 18:53:59  来源:igfitidea点击:

我们最喜欢Bash处理错误的方法是什么?
我在网上发现的处理错误的最佳示例是由William Shotts,Jr在http://www.linuxcommand.org上编写的。

他建议使用以下函数在Bash中进行错误处理:

#!/bin/bash

# A slicker error handling routine

# I put a variable in my scripts named PROGNAME which
# holds the name of the program being run.  You can get this
# value from the first item on the command line (
die() {
        echo 
        kill $$
}
). # Reference: This was copied from <http://www.linuxcommand.org/wss0150.php> PROGNAME=$(basename
set -e
) function error_exit { # ---------------------------------------------------------------- # Function for exit due to fatal program error # Accepts 1 argument: # string containing descriptive error message # ---------------------------------------------------------------- echo "${PROGNAME}: ${1:-"Unknown Error"}" 1>&2 exit 1 } # Example call of the error_exit function. Note the inclusion # of the LINENO environment variable. It contains the current # line number. echo "Example of error with line number and message" error_exit "$LINENO: An error has occurred."

我们是否在Bash脚本中使用了更好的错误处理例程?

解决方案

回答

我用过

0  success
 1  incorrect invocation or permissions
 2  system error (out of memory, cannot fork, no more loop devices)
 4  internal mount bug or missing nfs support in mount
 8  user interrupt
16  problems writing or locking /etc/mtab
32  mount failure
64  some mount succeeded

前;我认为因为某些原因,"出口"因我而失败。不过,上述默认设置似乎是个好主意。

回答

那是一个很好的解决方案。我只是想添加

tempfiles=( )
cleanup() {
  rm -f "${tempfiles[@]}"
}
trap cleanup 0

error() {
  local parent_lineno=""
  local message=""
  local code="${3:-1}"
  if [[ -n "$message" ]] ; then
    echo "Error on or near line ${parent_lineno}: ${message}; exiting with status ${code}"
  else
    echo "Error on or near line ${parent_lineno}; exiting with status ${code}"
  fi
  exit "${code}"
}
trap 'error ${LINENO}' ERR

作为基本的错误机制。如果一个简单的命令失败,它将立即停止脚本。我认为这应该是默认行为:由于此类错误几乎总是表示意想不到的事情,因此继续执行以下命令并不是真的"理智"。

回答

另一个考虑因素是要返回的退出代码。尽管bash本身使用了一些保留的退出代码,但只是" 1"是相当标准的,并且该页面还指出,用户定义的代码应在64-113范围内才能符合C / C ++标准。

我们可能还会考虑mount用于其退出代码的位向量方法:

temp_foo="$(mktemp -t foobar.XXXXXX)"
tempfiles+=( "$temp_foo" )

将代码"或者"在一起可以使脚本发出多个同时发生的错误信号。

回答

使用陷阱!

error ${LINENO} "the foobar failed" 2

...然后,无论何时创建临时文件:

#This function is used to cleanly exit any script. It does this displaying a
# given error message, and exiting with an error code.
function error_exit {
    echo
    echo "$@"
    exit 1
}
#Trap the killer signals so that we can exit with a good message.
trap "error_exit 'Received signal SIGHUP'" SIGHUP
trap "error_exit 'Received signal SIGINT'" SIGINT
trap "error_exit 'Received signal SIGTERM'" SIGTERM

#Alias the function so that it will print a message with the following format:
#prog-name(@line#): message
#We have to explicitly allow aliases, we do this because they make calling the
#function much easier (see example).
shopt -s expand_aliases
alias die='error_exit "Error 
#This is an example useage, it will print out
#Error prog-name (@1): Who knew false is false.
if ! /bin/false ; then
    die "Who knew false is false."
fi
(@`echo $(( $LINENO - 1 ))`):"'

$ temp_foo将在退出时被删除,并打印当前行号。 (set -e同样会给我们带来错误退出的行为,尽管它带有严重的警告并削弱了代码的可预测性和可移植性)。

我们可以让陷阱为我们调用"错误"(在这种情况下,它使用默认的退出代码1,不显示任何消息),也可以自行调用并提供明确的值;例如:

set -o errexit

将以状态2退出,并给出明确的消息。

回答

我更喜欢真正容易打电话的东西。因此,我使用的东西看起来有些复杂,但易于使用。我通常只是将以下代码复制并粘贴到脚本中。代码后面有一个解释。

set +e
echo "commands run here returning non-zero exit codes will not cause the entire script to fail"
echo "false returns 1 as an exit code"
false
set -e

我通常在error_exit函数旁边调用清除函数,但这在脚本之间有所不同,因此我将其省略。陷阱捕获常见的终止信号,并确保一切都清理干净。别名是真正的魔术。我喜欢检查一切是否失败。因此,总的来说,我将程序称为" if!"。类型声明。通过从行号中减去1,别名将告诉我发生故障的位置。它的调用也很简单,并且几乎是白痴的证明。下面是一个示例(只需将/ bin / false替换为我们要调用的任何内容)。

##代码##

回答

" set -e"的等效替代方法是

##代码##

它使标志的含义比" -e"更清楚。

随机添加:暂时禁用该标志,并返回默认值(无论退出代码如何,都继续执行),只需使用

##代码##

这排除了其他响应中提到的适当的错误处理,但是它是快速有效的(就像bash一样)。