bash - 提供给 bash 脚本或文件的选项中的错误检测

标签 bash

想象一下这样的命令行:

my-script -foo $(no-such-cmd)

$(no-such-cmd) 将导致错误,但我找不到方法让脚本知道存在问题。 set -e 似乎不起作用。有办法吗?

这是我在尝试处理文件中的参数时遇到的问题的一部分。 我正在逐行读取文件并尝试检测错误。

这是一个例子。我已经从文件中硬编码了几行假装行。

function err_handler
{
    ehs=1
    echo "bad thing happened"
}

function fxn
{
    echo fxn "$@"
    return 0
}

function doit
{
    ehs=0
    trap err_handler ERR
    eval fxn "$@"
    eval_status=$?
    trap "" ERR

    echo ehs $ehs ev $eval_status
}

line='-x good'
doit $line

line='-x $(no-such-cmd)'
doit $line

和输出:

> ./line.sh 
fxn -x good
ehs 0 ev 0
./line.sh: line 17: no-such-cmd: command not found
fxn -x
ehs 0 ev 0

请注意错误消息是如何显示的,但陷阱没有触发,退出状态也没有反射(reflect)它。

===============回答下面提出的一些问题===============

@Barmar - 扩展是在调用 doit 之前发生的,但它的工作是因为 doit_init。错误被捕获。我可以将此代码添加到我的实际代码中的循环中。

@user1934428 - 我想捕获从文件读取的开关中出现的任何错误。这个 $(no-such-cmd) 只是一种简单的方法来展示一个难以捕捉的方法。

@charles duffy - 我不是试图将代码放入变量中,而是从文件中读取命令的开关/参数。

@Willian Pursell - 我无法控制输入。我需要依赖用户正确引用参数。

最佳答案

所以永远不要打电话my-script -foo $(no-such-cmd) 。先进行变量赋值,然后调用命令。

if ! arg=$(no-such-cmd); then
   echo "no-such-cmd failed!"
   exit 2
fi
my-script -foo "$arg"

围绕 eval 破解您的解决方案和doittrap err $(..) 之间的进程间通信子 shell 和父 shell 将无法维护且不可读。无论如何,你必须分享你的trap ERR与子壳。而且,您必须进行从子 shell 到父 shell 的进程间通信 - 它们是单独的进程。在下面的示例中使用了一个信号。

此外,在下面,我将解析命令参数并在不同的阶段执行命令。请注意,变量赋值等于最后执行的命令。以args=($(no-such-cmd))为例最后执行的命令是 no-such-cmd在这种情况下,分配会失败并返回 127 退出代码。然而,args=($(no-such-cmd) $(true))最后执行的命令是 true ,在这种情况下,第一个子 shell 会向父 shell 发送 SIGUSR1 以传达有关错误的信息。

#!/bin/bash

err_handler() {
    # Just communicate to parent process that we failed.
    kill -s SIGUSR1 "$parent"
}

fxn() {
    echo fxn "$@"
}

doit_init() {
    trap err_handler ERR
    # just set variable err on SIGUSR1
    trap err=1 SIGUSR1
    # inherit ERR
    set -E
}

doit() {
    echo "+ fxn $*"
    err=0
    local args=()
    parent=$$
    eval "args=($*)"
    ret=$?
    if ((ret)); then
        echo "parsing args failed with $ret"
        return 1
    fi
    if ((err)); then
        echo "any command substitution while expanding arguments failed"
        return 1
    fi
    fxn "${args[@]}"
    ret=$?
    if ((ret)); then
        echo "Calling fxn failed $ret"
        return 1
    fi
}

doit_init

line='-x good'
doit "$line"

# shellcheck disable=SC2016
line='-x $(no-such-cmd)'
doit "$line"

# shellcheck disable=SC2016
line='-x $(no-such-cmd) $(true)'
doit "$line"

执行脚本的结果是:

$ ./script.sh 
+ fxn -x good
fxn -x good
+ fxn -x $(no-such-cmd)
./script.sh: line 30: no-such-cmd: command not found
parsing args failed with 127
+ fxn -x $(no-such-cmd) $(true)
./script.sh: line 30: no-such-cmd: command not found
any command substitution while expanding arguments failed

使用 shellcheck 检查您的脚本。不要将命令参数存储在字符串中 - 更喜欢 bash 数组。更喜欢引用命令替换。

关于bash - 提供给 bash 脚本或文件的选项中的错误检测,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77729270/

相关文章:

linux - dpn 的转换,从 windows 键入命令到 bash

python - 练习 Linux Shell 脚本编写

bash - 在没有 -i 选项的情况下使用默认值读取 bash 中的变量

linux - 移动 script.sh 到 bin

linux - 如何从 bash 脚本启动需要单独终端的 2 个程序?

bash - 将脚本调用中的变量插入脚本 bash 内的字符串中

git - Git Bash 和 GitHub for Windows shell 有什么区别?

regex - 相同的正则表达式用 sed 和用 grep 的评估不同

Bash seq 在for循环中产生两个独立的数字序列

bash - 如何检查 bash 脚本是在 Vagrant 机器内部还是外部执行的?