You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Eval可正常执行命令,但命令替换($())执行失败的原因解析

问题描述

以下是一段Shell脚本:

#!/bin/sh
echo "intented command:"
jq -n --argjson bar '{"test":"asdf"}' '$bar | .test'

foo="{\"test\":\"asdf\"}"
command="jq -n --argjson bar '$foo' '\$bar | .test'"
echo "Built command:"
echo $command
echo "Command execution with eval:"
eval $command
echo "Command execution with command substitution:"
$($command)

脚本中第一段直接执行的jq命令可正常运行;echo输出的构建命令和第一段原命令完全一致。但使用eval $command执行正常,使用$($command)执行时却报错:

jq: invalid JSON text passed to --argjson
Use jq --help for help with command-line options,
or see the jq manpage, or online docs  at https://stedolan.github.io/jq

原因分析

核心差异在于Shell对两种执行方式的解析逻辑完全不同:

  1. eval $command的解析过程:
    eval会将变量command中的内容当作完整Shell命令重新解析一遍。此时Shell会识别字符串中的单引号语法:

    • '{"test":"asdf"}'会被处理为去掉单引号的纯JSON字符串{"test":"asdf"},正确传递给jq的--argjson参数;
    • '$bar | .test'会被处理为去掉单引号的表达式$bar | .test,作为jq的过滤参数。整个命令的参数拆分完全符合预期。
  2. $($command)的解析过程:
    $()是命令替换语法,执行流程是:

    • 先展开$command得到完整字符串:jq -n --argjson bar '{"test":"asdf"}' '$bar | .test';
    • Shell会对这个字符串进行分词处理,但不会解析其中的单引号语法——单引号会被当作普通字符保留在参数中。
      最终拆分出的参数会变成:jq、-n、--argjson、bar、'{"test":"asdf"}'(带单引号的字符串)、'$bar、|、.test'。
      这就导致jq收到的--argjson参数是带单引号的'{"test":"asdf"}',不符合JSON格式,因此报错。

正确的做法

不要用字符串存储命令,而是用数组保存命令和参数,Shell会自动正确处理每个参数的边界:

#!/bin/sh
echo "intented command:"
jq -n --argjson bar '{"test":"asdf"}' '$bar | .test'

foo='{"test":"asdf"}'
# 用数组存储命令及参数
command=(jq -n --argjson bar "$foo" '$bar | .test')
echo "Built command:"
echo "${command[@]}"
# 正确执行数组中的命令
echo "Command execution with array:"
"${command[@]}"

数组方式可以避免所有转义和引号解析的问题,是Shell中存储复杂命令的标准做法。

内容的提问来源于stack exchange,提问作者Agyss

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 15:40:55