Azure Pipeline中convertToJson为何不对宏语法变量值转义?
为什么Azure YAML管道中只有
fooRuntimeVariable的值无法被convertToJson正确转义? 主管道代码
parameters: - name: foo type: string default: 'b"ar' trigger: none pool: vmImage: 'ubuntu-latest' variables: - name: foo value: 'b"ar' steps: - checkout: none - template: /pipelines/steps/set-settings.yaml parameters: settings: fooLiteral: 'b"ar' fooParameter: ${{ parameters.foo }} fooCompiledVariable: ${{ variables.foo }} fooRuntimeVariable: $(foo)
模板文件 /pipelines/steps/set-settings.yaml
parameters: - name: settings type: object steps: - script: | echo $SETTINGS displayName: 'Display settings' env: SETTINGS: ${{ convertToJson(parameters.settings) }}
原因分析
问题核心在于Azure管道的编译时与运行时变量替换时机差异,以及convertToJson函数的执行阶段:
编译阶段处理的项(可正确转义):
fooLiteral:直接定义的字符串字面量,编译时就被解析为b"ar,convertToJson在编译时处理该字符串,自动转义双引号为\",最终生成合法的JSON值"b\"ar"。${{ parameters.foo }}和${{ variables.foo }}:使用${{ }}编译时表达式,管道编译阶段就会将参数/变量的实际值(b"ar)替换进去,convertToJson同步在编译时处理这些已经替换完成的字符串,完成正确转义。
运行阶段处理的项(无法正确转义):
fooRuntimeVariable: $(foo):使用$( )运行时变量引用语法,这种变量的替换要等到脚本实际执行的运行阶段才会发生。而convertToJson是编译阶段执行的函数,此时$(foo)还只是一个未替换的字符串字面量,里面的$、(、)不属于JSON需要转义的特殊字符,所以convertToJson直接将其处理为"$(foo)"。- 等到脚本运行时,
$SETTINGS中的$(foo)才会被替换为b"ar,但此时JSON结构已经在编译阶段生成,替换后的字符串里的双引号没有被转义,导致最终的JSON格式错误。
解决方案(可选)
如果需要让运行时变量也能生成合法的JSON,需要将JSON转换逻辑放到运行时执行,比如使用jq工具在脚本中处理:
steps: - script: | # 用jq生成包含运行时变量的合法JSON SETTINGS=$(jq -n --arg foo "$(foo)" '{fooRuntimeVariable: $foo}') echo $SETTINGS displayName: 'Display settings with runtime JSON escape'
内容的提问来源于stack exchange,提问作者Rui Jarimba
相关产品推荐
相关产品推荐

