Bash与Ksh运行脚本输出JSON格式不一致问题咨询
问题根因
该问题和curl请求无关,核心是Bash与Ksh的Shell展开顺序存在实现差异,且脚本中未对变量引用加双引号触发了该差异:
- 两种Shell的展开顺序区别:
- Bash默认执行顺序:大括号展开 → 变量展开 → 字段拆分 → 路径展开
- Ksh(含ksh88/ksh93/pdksh/mksh等主流分支)默认执行顺序:变量展开 → 大括号展开 → 字段拆分 → 路径展开
- 触发逻辑:当你写
echo $access_token_response时,变量未加双引号,Ksh会先把变量内容完整展开,此时变量值中首尾带大括号、字段间带逗号的JSON字符串,刚好匹配Ksh的大括号展开语法规则——大括号包裹、内部用逗号分隔的内容会被展开为多个独立词,首尾的大括号会被作为语法标记移除,内部逗号替换为词分隔符。而Bash因为大括号展开在变量展开之前执行,变量值里的大括号不会被识别为语法标记,因此内容能完整保留。
正规解决方案
该修复是POSIX标准规定的标准写法,不属于兼容hack:
- 所有需要保留原值的变量引用,必须包裹双引号,双引号内的变量展开后不会触发大括号展开、字段拆分、路径扩展等逻辑,内容会原封不动保留。
- 替换echo为printf输出任意包含特殊字符的内容,echo的转义、参数解析行为在不同Shell中没有统一标准,printf的行为是POSIX明确规范的,跨Shell表现完全一致。
- 补充curl参数优化:去掉-i参数(该参数会把HTTP响应头混入返回内容,导致后续JSON解析失败),增加-s参数关闭curl的进度条输出,避免无关内容干扰结果。
修正后的可跨Shell运行的脚本如下:
access_token_response=$(curl -k --tlsv1.2 -s -X POST https://api.cloud.com/oauth2/token -d client_id=id -d client_secret=secret -d grant_type=client_credentials) printf '%s\n' "$access_token_response"
Bash与Ksh常见差异及跨Shell适配指引
以下为实际生产环境中高频触发的差异点,适配规则可直接复用:
- 展开顺序差异:即本次问题触发的根因。适配规则:任何场景下引用非单词类变量(尤其是包含JSON、路径、特殊符号的内容)一律加双引号,不要依赖不同Shell的默认展开顺序。
- echo行为差异:Bash默认echo不解析反斜杠转义字符,Ksh默认echo会自动解析\n、\t等转义序列,部分Ksh版本还会把echo后带-开头的字符串识别为命令选项。适配规则:所有输出场景优先使用printf代替echo,避免转义、参数解析异常。
- 数组实现差异:Bash数组默认从0下标开始,用
arr=("a" "b")格式定义;Ksh93兼容Bash数组写法,但ksh88、pdksh等老版本Ksh数组默认从1下标开始,需要用set -A arr "a" "b"格式定义,且关联数组的实现语法和Bash完全不兼容。适配规则:跨Shell脚本尽量避免使用数组,必须使用时用位置参数(set -- "val1" "val2"定义,通过$1、$@取值)实现,该写法所有POSIX兼容Shell均支持。 - 函数定义差异:Bash支持
function func() { ... }的带function关键字的写法,部分精简版Ksh不识别该写法。适配规则:函数定义统一使用func() { ... }的POSIX标准写法,省略function关键字。 - 内置字符串处理差异:Bash支持
${var:offset:length}切片、${var/pattern/replace}替换等扩展参数展开语法,Ksh93部分支持但匹配规则存在差异,老版本Ksh完全不支持该类语法。适配规则:跨Shell场景下的字符串处理逻辑,优先使用awk、sed、cut等标准外部工具实现,不要依赖Shell内置的扩展参数展开。 - 扩展通配符差异:Bash需要手动执行
shopt -s extglob才支持*(pattern)、?(pattern)等扩展通配符,Ksh默认开启部分扩展通配符规则,匹配逻辑存在差异。适配规则:用到通配符匹配时,显式设置对应Shell选项,不要依赖默认配置。
跨Shell脚本开发核心原则:严格遵循POSIX Shell标准编写逻辑,不依赖任意单个Shell的默认行为或专属扩展特性,即可覆盖绝大多数跨Shell运行场景。
内容的提问来源于stack exchange,提问作者A Campos
相关产品推荐
相关产品推荐

