我对 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)
您可以简单地在语句中结束函数,然后函数的返回码将是语句本身的返回码。
支票[[ $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)