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

Nat*_*ouk 1 bash shell-script

我对 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
Run Code Online (Sandbox Code Playgroud)

fil*_*den 5

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

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

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

最后一点是,您可能需要考虑在脚本中启用选项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
Run Code Online (Sandbox Code Playgroud)

正如@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
Run Code Online (Sandbox Code Playgroud)