Bash脚本中使用jq执行数值运算报错,寻求解决方案
问题分析与解决
报错核心原因
你遇到的Invalid numeric literal错误,根源是变量引用和jq参数传递的问题:
- 用单引号定义
Args变量时,$Port不会被Bash解析替换,导致jq收到的是字符串$Port而非数字1050,无法解析为合法数值。 - 脚本还存在其他逻辑缺陷,比如循环覆盖JSON内容、键名语法错误、重复查询hosts等,都会导致不符合预期或报错。
修正后的完整脚本
JSFl="/tmp/test.json" Port=1050 # 初始化空JSON对象(文件不存在或为空时执行) [[ ! -s "$JSFl" ]] && echo '{}' > "$JSFl" for Srv in $(cat /tmp/servers); do # 单次查询hosts,同时获取IP和主机名,减少IO操作 read -r IPn NodeNm <<< "$(grep "$Srv" /etc/hosts | awk '{print $1, $2}')" # 用数组存储jq参数,避免字符串分词导致的参数混乱 Args=( --arg S "$Srv" --arg Node "$NodeNm" --arg IP "$IPn" --argjson P1 "$Port" # 直接传递数字类型,无需后续转换 ) # 使用jq的+=操作合并对象,保留原有内容并新增键值对 jq "${Args[@]}" '. += {$S: {"Node": $Node, "PubIP": $IP, "Port1": $P1, "Port2": $P1-1, "Port3": $P1+2}}' "$JSFl" > tmp.json && mv -f tmp.json "$JSFl" done
关键修正说明
- 参数传递优化:用数组存储jq参数,避免字符串分词错误;用
--argjson直接传递数字类型的端口值,省去tonumber转换步骤。 - JSON合并逻辑:用
. += {$S: ...}实现新增键值对,而非每次覆盖整个文件,保证循环能添加多个服务器的数据。 - hosts查询优化:单次完成grep+awk操作,并用
read同时读取IP和主机名,提升执行效率。 - 空文件处理:提前生成空JSON对象
{},避免第一次执行时jq处理空文件的异常。 - 语法修正:Bash循环关键字为小写
for,原脚本的For会被当作未知命令报错;jq中用{$S}动态生成键名,原脚本的"$S"会把字符串$S作为键名,而非变量值。
预期生成结果
执行脚本后,/tmp/test.json会生成符合要求的多字段JSON:
{ "Comp1": { "Node": "C1VM1", "PubIP": "1.2.3.4", "Port1": 1050, "Port2": 1049, "Port3": 1052 }, "Comp2": { "Node": "C2VM1", "PubIP": "5.6.7.8", "Port1": 1050, "Port2": 1049, "Port3": 1052 } }
内容的提问来源于stack exchange,提问作者DenisZ
相关产品推荐
相关产品推荐

