如何在 shell 脚本中将变量设置为变量内部的变量
How to set a variable to the variable inside a variable in a shell script
我需要编写一个 POSIX shell 脚本来更改系统配置。在这样做之前,我想确保我编辑的任何文件都有备份。
此脚本的要求是使用 dmenu
提示用户是否已安装,如果未安装则使用 read
。
我想要一个函数(下面命名为 communicate
),它会根据在 运行、$dmenu.[=20 上设置的变量自动为我处理这个问题=]
我在向变量内部的变量写入时遇到问题,如下所示:
#!/usr/bin/env sh
[ $(command -v dmenu 2>/dev/null) ] && dmenu='true'
communicate(){
description=""; options=""; outcome=""
if [ $dmenu ]; then
echo "$(printf "$options" | dmenu -i -p "$description")" >&0 | read $outcome
else
printf "$description $options "; read $outcome
fi
}
backup(){
[ ] && file="" || communicate 'Enter file: ' '' 'file'
[ ! -f $file ] && backup ""
cp "$file" "$file.bak"
}
select_interface(){
[ ] && interface="" || communicate 'Select interface:' "$interfaces" 'interface'
}
backup
想要将用户输入保存到名为 $file 的变量中,而稍后 select_interface
想要保存到名为 [=28= 的变量中]$接口。
如果未安装 dmenu
,则写入 $outcome 可以与 else 语句一起正常工作,而如果已安装,我似乎无法在传递 [=12= 的结果时触发 read
命令] 通过 STDIN 重定向进入读取,它在脚本之外工作。
有人能看出我做错了什么或者我如何才能做得更好吗?
我需要所有这些都在一个函数 communicate 中,作为与用户的通信代理。
声明
echo "$(printf "$options" | dmenu -i -p "$description")" >&0 | read $outcome
作为管道,导致 shell 将 echo
和 read
作为 2 个单独的进程实现。 read
仍然是一个分叉shell,它仍然设置变量$outcome
,但它只在分叉shell中设置它,而不是在分叉(父)shell.
技术上正确的方法是:
eval $outcome=$\(printf "$options" \| dmenu -i -p "$description"\)'
但是 除了一次性代码,我建议不要使用 eval。
我也反对接受变量名设置的函数,这很难做到正确。
更简洁的方法:
#!/usr/bin/env sh
if [ $(command -v dmenu 2>/dev/null) ]; then
communicate() {
description=""
options=""
# 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=""
options=""
if [ -n "$options" ]; then
optstring="options: ${options}; "
else
optstring=""
fi
read -p "${optstring}${description}: " outcome
echo $outcome
}
fi
backup() {
if [ -n "" ]; then
file=""
else
file=$(communicate 'Enter file')
fi
if [ -f "$file" ]; then
cp "$file" "${file}.bak"
else
backup
fi
}
select_interface() {
if [ -n "" ]; then
interface=""
else
interface=$(communicate "Enter interface" "$interfaces")
fi
}
我需要编写一个 POSIX shell 脚本来更改系统配置。在这样做之前,我想确保我编辑的任何文件都有备份。
此脚本的要求是使用 dmenu
提示用户是否已安装,如果未安装则使用 read
。
我想要一个函数(下面命名为 communicate
),它会根据在 运行、$dmenu.[=20 上设置的变量自动为我处理这个问题=]
我在向变量内部的变量写入时遇到问题,如下所示:
#!/usr/bin/env sh
[ $(command -v dmenu 2>/dev/null) ] && dmenu='true'
communicate(){
description=""; options=""; outcome=""
if [ $dmenu ]; then
echo "$(printf "$options" | dmenu -i -p "$description")" >&0 | read $outcome
else
printf "$description $options "; read $outcome
fi
}
backup(){
[ ] && file="" || communicate 'Enter file: ' '' 'file'
[ ! -f $file ] && backup ""
cp "$file" "$file.bak"
}
select_interface(){
[ ] && interface="" || communicate 'Select interface:' "$interfaces" 'interface'
}
backup
想要将用户输入保存到名为 $file 的变量中,而稍后 select_interface
想要保存到名为 [=28= 的变量中]$接口。
如果未安装 dmenu
,则写入 $outcome 可以与 else 语句一起正常工作,而如果已安装,我似乎无法在传递 [=12= 的结果时触发 read
命令] 通过 STDIN 重定向进入读取,它在脚本之外工作。
有人能看出我做错了什么或者我如何才能做得更好吗? 我需要所有这些都在一个函数 communicate 中,作为与用户的通信代理。
声明
echo "$(printf "$options" | dmenu -i -p "$description")" >&0 | read $outcome
作为管道,导致 shell 将 echo
和 read
作为 2 个单独的进程实现。 read
仍然是一个分叉shell,它仍然设置变量$outcome
,但它只在分叉shell中设置它,而不是在分叉(父)shell.
技术上正确的方法是:
eval $outcome=$\(printf "$options" \| dmenu -i -p "$description"\)'
但是 除了一次性代码,我建议不要使用 eval。
我也反对接受变量名设置的函数,这很难做到正确。
更简洁的方法:
#!/usr/bin/env sh
if [ $(command -v dmenu 2>/dev/null) ]; then
communicate() {
description=""
options=""
# 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=""
options=""
if [ -n "$options" ]; then
optstring="options: ${options}; "
else
optstring=""
fi
read -p "${optstring}${description}: " outcome
echo $outcome
}
fi
backup() {
if [ -n "" ]; then
file=""
else
file=$(communicate 'Enter file')
fi
if [ -f "$file" ]; then
cp "$file" "${file}.bak"
else
backup
fi
}
select_interface() {
if [ -n "" ]; then
interface=""
else
interface=$(communicate "Enter interface" "$interfaces")
fi
}