如何将Bash变量传入jq过滤器并将完整命令存入变量?
问题描述
我有一个示例JSON文件/tmp/test.json,内容如下:
{ "foo" : "val1", "bar" : "val2" }
将键名存入变量后,执行以下命令能正常输出对应值:
key=foo cat /tmp/test.json | jq --arg k $key '.[$k]' # 输出:"val1"
但尝试将完整命令存入变量CMD后执行eval "$CMD"时出现错误:
CMD=`echo cat /tmp/test.json | jq --arg k $key '.[$k]'` eval "$CMD" # 错误输出:jq: parse error: Invalid numeric literal at line 1, column 4
请问该如何正确转义特殊字符,让命令存入变量后能正常执行?
解决方法
方法1:双引号包裹并转义特殊字符
直接用双引号定义变量,转义内部的$避免shell提前解析,同时保留jq所需的单引号:
key=foo CMD="cat /tmp/test.json | jq --arg k $key '\.[\$k]'" # 查看命令内容 echo "$CMD" # 执行命令 eval "$CMD"
这里的\$k会在eval执行时被解析为$k,正确传递给jq作为参数。
方法2:Here-doc赋值(免复杂转义)
用here-doc可以原样写入命令,避免手动转义:
key=foo # 单引号包裹EOF,内部变量不会提前解析 read -r -d '' CMD <<'EOF' cat /tmp/test.json | jq --arg k "$key" '.[$k]' EOF # 查看命令 echo "$CMD" # 执行命令 eval "$CMD"
如果需要提前替换$key的值,去掉EOF的单引号即可(写成<<EOF)。
方法3:数组存储命令(推荐)
在bash中用数组存储命令是更安全的方式,能避免eval带来的潜在风险:
key=foo # 存储完整命令(包含管道) CMD=(cat /tmp/test.json '|' jq --arg k "$key" '.[$k]') # 执行命令 eval "${CMD[*]}" # 更稳妥的拆分方式(避免eval处理管道) JQ_CMD=(jq --arg k "$key" '.[$k]') cat /tmp/test.json | "${JQ_CMD[@]}"
数组能自动处理参数中的特殊字符,无需手动转义。
错误原因分析
你最初的命令出错有两个核心原因:
- 反引号执行时,管道会被提前解析,
echo cat /tmp/test.json的输出被当作jq的输入,而非把整个cat ... | jq ...作为命令字符串存入变量; - 单引号在反引号环境中被shell解析,导致jq接收到的参数被提前替换(比如
.[$k]变成.[foo]),破坏了jq的语法结构。
内容的提问来源于stack exchange,提问作者hydradon
相关产品推荐
相关产品推荐

