我需要编写一个POSIX Shell脚本,它将更改系统配置。在这样做之前,我想确保我编辑的任何文件都有备份。此脚本的要求是使用dmenu
提示用户(如果已安装),如果使用read
则提示。我想要一个函数(在下面称为communicate
),该函数将根据在运行时设置的变量$ dmenu。
我在写入变量内部的变量时遇到问题,如下所示:
#!/usr/bin/env sh
[ $(command -v dmenu 2>/dev/null) ] && dmenu='true'
communicate(){
description="$1"; options="$2"; outcome="$3"
if [ $dmenu ]; then
echo "$(printf "$options" | dmenu -i -p "$description")" >&0 | read $outcome
else
printf "$description $options "; read $outcome
fi
}
backup(){
[ $1 ] && file="$1" || communicate 'Enter file: ' '' 'file'
[ ! -f $file ] && backup "$1"
cp "$file" "$file.bak"
}
select_interface(){
[ $1 ] && interface="$1" || communicate 'Select interface:' "$interfaces" 'interface'
}
backup
希望将用户输入保存到名为$ file的变量,而后来select_interface
希望将用户输入保存到名为$ interface的变量。如果未安装dmenu
,则在else语句中写入$ outcome可以正常工作,而如果已安装,当通过STDIN重定向传递read
的结果时,似乎无法获得dmenu
命令来触发读入,可以在脚本之外使用。
有人可以看到我在做错什么,还是可以做得更好?我需要所有这些都在一个功能communicate中,充当与用户的通信代理。
声明
echo "$(printf "$options" | dmenu -i -p "$description")" >&0 | read $outcome
是管道,使外壳将echo
和read
实现为2个单独的进程。 read
仍然是一个派生的shell,它仍然设置变量$outcome
,但是它仅在派生的shell中设置它,而不是在派生的(父)shell中设置它。
技术上正确的方法是:
eval $outcome=\$\(printf "$options" \| dmenu -i -p "$description"\)'
[BUT我建议不要使用eval来获得一次性代码。
我还建议不要接受接受设置变量名的函数,很难正确设置。
更干净的方法:
#!/usr/bin/env sh
if [ $(command -v dmenu 2>/dev/null) ]; then
communicate() {
description="$1"
options="$2"
# also fixed this bug with the menu selection, each option needs to be in a new line
printf "%s\n" $options | dmenu -i -p "${description}:"
}
else
communicate() {
description="$1"
options="$2"
if [ -n "$options" ]; then
optstring="options: ${options}; "
else
optstring=""
fi
read -p "${optstring}${description}: " outcome
echo $outcome
}
fi
backup() {
if [ -n "$1" ]; then
file="$1"
else
file=$(communicate 'Enter file')
fi
if [ -f "$file" ]; then
cp "$file" "${file}.bak"
else
backup
fi
}
select_interface() {
if [ -n "$1" ]; then
interface="$1"
else
interface=$(communicate "Enter interface" "$interfaces")
fi
}