Bash/JQ脚本问题:无法替换JSON命令中的变量为实际值
问题描述
编写脚本时,从JSON文件提取play_music命令,该命令包含device_directory、music_file、audio_type等变量。脚本已成功读取这些变量的实际值,但提取出的命令中的变量未被替换为对应值,输出仍为原始模板字符串。
data.json片段
"play_music": { "name": "play_music", "command" : "adb shell am start -a android.intent.action.VIEW -d file:///${device_directory}${music_file} -t audio/${audio_type}", "variables" : { "device_directory" : { "name" : "device_directory", "variable" : "/Downloads/" }, "music_file" : { "name" : "music_file", "variable" : "test.mp3" }, "audio_type" : { "name" : "audio_type", "variable" : "mp3" } }
脚本片段
while true; do variables=( $(jq -r ".commands.$option.variables | map(.name) | .[]" data.json)) ##echo ${variables[@]} for i in ${variables[@]}; do ##echo -e "\n$i" var=$(jq -r ".commands.$option.variables.$i.variable" data.json) if [[ -z $var ]]; then ##echo -e "var is empty" echo -e "Enter $i: " read $i else ##echo -e "var is full" declare $i=$var fi done echo -e "\nmusic file='${music_file}'" echo -e "\naudio type='${audio_type}'" echo "" command="$(jq -r ".commands.$option.command" data.json)" echo $command
当前变量已正确读取:
music file='test.mp3' audio type='mp3'
但输出的命令仍为:adb shell am start -a android.intent.action.VIEW -d file:///${device_directory}${music_file} -t audio/${audio_type}
期望输出:adb shell am start -a android.intent.action.VIEW -d file:////Downloads/test.mp3 -t audio/mp3
解决方案
核心原因是直接从JSON读取的命令字符串是静态文本,Bash不会自动解析其中的变量。以下是三种可行的解决方法:
方法一:使用eval执行变量替换
修改读取命令的代码行,让Bash解析命令字符串中的变量:
command=$(eval echo "$(jq -r ".commands.$option.command" data.json)")
说明:先通过jq获取原始命令模板,再用eval echo触发Bash的变量替换逻辑,将模板中的变量替换为实际值。
方法二:使用envsubst工具(更安全)
如果系统安装了envsubst(通常属于gettext包),可以用它来替换环境变量。首先需要将脚本中定义的shell变量导出为环境变量:
# 在循环中定义变量时,改用export或declare -x for i in ${variables[@]}; do var=$(jq -r ".commands.$option.variables.$i.variable" data.json) if [[ -z $var ]]; then echo -e "Enter $i: " read $i else export $i=$var # 导出为环境变量 fi done
然后读取并替换命令:
command=$(jq -r ".commands.$option.command" data.json | envsubst)
说明:envsubst会自动识别并替换字符串中的环境变量,避免了eval带来的安全风险。
方法三:手动遍历变量替换
通过Bash的参数替换功能,逐个替换模板中的变量:
command=$(jq -r ".commands.$option.command" data.json) # 遍历所有变量,替换模板中的${变量名} for i in "${variables[@]}"; do command=${command//\$\{$i\}/${!i}} done
说明:${command//\$\{$i\}/${!i}}是Bash的全局替换语法,${!i}是间接引用,获取变量i对应的实际值。
执行任意一种方法后,输出的command都会替换为期望的结果。
内容的提问来源于stack exchange,提问作者Siân

