使用 $? 在 bash 中?

Joh*_*han 10 shell bash error-handling

当我阅读有关 $? 另一个问题浮现在脑海中。

是否有关于如何使用 $ 的最佳实践?在 bash 中?


让我们举个例子:

我们有一个线性脚本,我想知道所有命令都执行正常。你认为调用一个小函数(我们称之为“did_it_work”)来检查错误代码是否可以,如果不是就中断。

#!/bin/bash 

function did_it_work {
    code=$1
    if [ "$code" -ne "0" ]
    then
        echo "Error failure: code $code "
        exit 1
    fi
}

dir=some/path

mkdir -p $dir
did_it_work $? 

cd $dir
did_it_work $? 

run_some_command
did_it_work $? 
Run Code Online (Sandbox Code Playgroud)

这种方法当然意味着我必须手动解决问题(如果有的话)并重新运行脚本。

您认为这是一个好主意还是有其他一些最佳实践可以做到这一点?

/谢谢

Mik*_*kel 15

一种常见的方式是:

die() {
    IFS=' ' # make sure "$*" is joined with spaces

    # output the arguments if any on stderr:
    [ "$#" -eq 0 ] || printf '%s\n' "$*" 1>&2
    exit 1
}
Run Code Online (Sandbox Code Playgroud)

然后你像这样使用它:

mkdir -p some/path || die "mkdir failed with status $?"
Run Code Online (Sandbox Code Playgroud)

或者,如果您希望它包含退出状态,您可以将其更改为:

die() {
    last_exit_status=$?
    IFS=' '
    printf '%s\n' "FATAL ERROR: $* (status $last_exit_status)" 1>&2
    exit 1
}
Run Code Online (Sandbox Code Playgroud)

然后使用它会更容易一些:

mkdir -p some/path || die "mkdir failed"
Run Code Online (Sandbox Code Playgroud)

当它失败时,mkdir很可能已经发出了一条错误消息,因此第二个可能被视为多余的,您可以这样做:

mkdir -p some/path || exit   # with the same (failing) exit status as mkdir's
mkdir -p some/path || exit 1 # with exit status 1 always
Run Code Online (Sandbox Code Playgroud)

(或die不加参数使用上述第一个变体)

以防万一你以前没见过command1 || command2,它运行command1,如果command1失败,它运行command2

所以你可以像“创建目录或死亡”一样阅读它。

您的示例如下所示:

mkdir -p some/path || die "mkdir failed"
cd some/path || die "cd failed"
some_command || die "some_command failed"
Run Code Online (Sandbox Code Playgroud)

或者你可以dies在右边进一步对齐,这样主要代码就更明显了。

mkdir -p some/path         || die "mkdir failed"
cd some/path               || die "cd failed"
some_command               || die "some_command failed"
Run Code Online (Sandbox Code Playgroud)

或者当命令行很长时在以下行:

mkdir -p some/path ||
  die "mkdir failed"

cd some/path ||
  die "cd failed"

some_command ||
  die "some_command failed"
Run Code Online (Sandbox Code Playgroud)

此外,如果您要some/path多次使用该名称,请将其存储在一个变量中,这样您就不必一直键入它,并且可以在需要时轻松更改它。并且在将变量参数传递给命令时,请确保使用--选项分隔符,这样如果参数以-.

dir=some/path
mkdir -p -- "$dir"         || die "Cannot make $dir"
cd -P -- "$dir"            || die "Cannot cd to $dir"
some_command               || die "Cannot run some_command"
Run Code Online (Sandbox Code Playgroud)


War*_*ung 9

你可以像这样重写你的代码:

#!/bin/bash
function try {
    "$@"
    code=$?
    if [ $code -ne 0 ]
    then
        echo "$1 did not work: exit status $code"
        exit 1
    fi
}

try mkdir -p some/path
try cd some/path
try run_some_command
Run Code Online (Sandbox Code Playgroud)

如果您实际上不需要记录错误代码,而只需要记录命令是否成功,您可以try()像这样进一步缩短:

function try {
    if ! "$@"
    then
        echo "$1 did not work"
        exit 1
    fi
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*n D 8

如果您真的想exit出错并且正在使用 Bash,那么您还应该考虑set -e. 来自help set

-e 如果命令以非零状态退出,则立即退出。

这当然不会为您提供 did_it_work() 函数的灵活性,但它是一种确保您的 bash 脚本在出现错误时停止的简单方法,而无需向您的新函数添加大量调用。

  • 此外,即使你使用了 `set -e`,你也可以设置一个“异常处理程序”来捕获所有带有 `trap did_it_work EXIT` 的错误。 (2认同)