如何在惯用的 bash 中编写这个函数?

如何在惯用的 bash 中编写这个函数?

我对 Bash shell 脚本非常陌生,最终我得到了以下代码。我无法让它以任何其他方式工作,但我觉得这是非常草率的代码。

有人可以帮我用惯用的(安全、简洁、易于阅读)Bash 重新编写这段代码吗?我特别想知道是否有更好的方法从函数返回值,以及我是否在 if 语句中正确测试它。 (if 语句应该使用括号吗?)

#!/bin/bash

input() {
    read -p $'\e[31m\e[1m'"$1"$' [Y/n] \e[0m' -n 1 -r
    echo
    if [[ $REPLY =~ ^[Yy]$ ]]
    then
        return 0
    fi
    return 1
}

if input "Upgrade Arch?"
then
    sudo pacman -Syu
fi

答案1

您可以简单地在语句中结束函数,然后函数的返回码将是语句本身的返回码。

支票[[ $REPLY =~ ^[Yy]$ ]]看起来不错。由于您使用的是 bash 内置双方括号测试,因此不需要引用变量,因为它们内部不会发生分词。使用正则表达式检查很好,尽管更简单的全局匹配[[ $REPLY = [Yy] ]]也可以。

我想我要做的主要其他更改是在函数中使用局部变量并将其传递给read.您可以将其标记为 atlocal或 use declare,这使得变量在函数内部使用时成为局部变量。

最后一点是,您可能需要考虑在脚本中启用选项errexit( -e)、nounset( -u) 和以使它们更安全。pipefail这将阻止脚本在命令失败后继续运行,并有助于捕获变量名称中的拼写错误。在这些情况下,您需要更加防御性地进行编码(显式测试大多数命令中的错误,使用可能未设置的环境变量的默认值),但是当出现意外中断时,它可以更轻松地进行调试。

把它们放在一起:

#!/bin/bash

set -eu -o pipefail

input() {
    declare confirm
    declare -r prompt=${1:-"Confirm?"}
    read -p $'\e[31m\e[1m'"${prompt}"$' [Y/n] \e[0m' -n 1 -r confirm
    echo
    [[ $confirm =~ ^[Yy]$ ]]
}

if input "Upgrade Arch?" ; then
    sudo pacman -Syu
fi

并作为@StéphaneChazelas建议,您甚至可以进一步改进它:

set -eu -o pipefail

input() {
    declare confirm
    declare -r prompt=${1:-"Confirm?"}
    IFS= read -p $'\e[31;1m'"${prompt}"$' [Y/n] \e[m' -n 1 -r confirm
    echo >&2
    [[ $confirm = [Yy] ]]
}

if input "Upgrade Arch?" ; then
    sudo pacman -Syu
fi

相关内容