在PowerShell中如何在JSON字符串中添加变量

11

我在我的PowerShell脚本中有如下的JSON代码。 将$variable设置为1111111111。

$jsonfile = '{"Version": "2012-10-17","Statement": {"Effect": "Allow","Action": "sts:AssumeRole","Resource": "arn:aws:iam::$variable:role/xxxxxx"}}'

输出结果为....arn:aws:iam::$variable:role/xxxxxx.....而不是....arn:aws:iam::1111111111:role/xxxxxx

问题在于我必须在JSON字符串中使用单引号,否则将会出错。如果我使用单引号,我就无法将变量放到字符串里面。我该如何解决这个问题?

3个回答

10

有很多方法可以解决您的问题,但可能最简单的方法是使用PowerShell的字符串插值

  • 整体使用双引号字符串以启用嵌入式变量引用和子表达式的插入($(...))。

  • 将嵌入的"字符转义为`"(使用反引号)。

  • 通过在{...}中括住变量名称来消除变量引用的歧义。

示例:

PS> $variable='111'
PS> "{`"Version`": `"arn:aws:iam::${variable}:role/xxxxxx`"}}"
{"Version": "arn:aws:iam::111:role/xxxxxx"}}

请注意,在插值字符串中将变量名用{...}括起来只有在下一个字符可能被误解为变量名的一部分时才是必要的。
跟在变量名后面的:就是这种情况,因为PS变量可以有一个作用域说明符,它位于变量名之前,并用:与变量名分隔开,例如$env:USERNAME
DAXaholic提供的有用答案显示了一种基于PowerShell的二进制-f运算符的替代方法,它本质上与.NET框架的String.Format方法相同:

  • 它引入了额外的复杂性,例如需要知道它的转义规则({字符必须转义为{{),以及如何格式化指定在-r右侧的参数({0}指的是第一个RHS参数,...)

  • 另一方面,-f提供了许多复杂的格式化选项。

此外,考虑使用他的答案演示的Convert*-Json cmdlet:尽管它们的性能较差,但它们最终使JSON的操作更加容易和稳健。


在原生PowerShell代码领域中的替代方法:

  • 使用二进制+运算符进行字符串连接:
'{"Version": "arn:aws:iam::' + $variable + ':role/xxxxxx"}}'
  • 使用$ExecutionContext.InvokeCommand.ExpandString()实现字符串模板:
$variable='111'    
$tmpl = '{"Version": "arn:aws:iam::${variable}:role/xxxxxx"}}' # string template *literal*
$ExecutionContext.InvokeCommand.ExpandString($tmpl) # performs on-demand interpolation

5
另一个解决方案是:
$jsonfile = '{{"Version": "2012-10-17","Statement": {{"Effect": "Allow","Action": "sts:AssumeRole","Resource": "arn:aws:iam::{0}:role/xxxxxx"}}}}' -f $variable  

所以你必须使用另一个大括号来转义大括号,但在你的情况下,大括号比引号少,因此它是“较少的混淆” :)

在你的情况下,最简单的解决方案可能就是将字符串连接起来,而不是使用字符串格式化/插值。

此外,您还可以使用JSON cmdlets来处理:

$jsonfile | 
    ConvertFrom-Json | 
    % { $_.Statement.Resource = "arn:aws:iam::${variable}:role/xxxxxx"; $_ } | 
    ConvertTo-Json

这是一个可行的解决方案,但现在你要处理另一种语言的转义和格式化规则(PowerShell的二进制-f运算符本质上是.NET框架的String.Format方法,这增加了复杂性。 - mklement0
1
嗯,但在我个人看来,它比转义所有其他字符更易读,并且通常代码更多是被阅读而非重写的。无论如何,我认为两种解决方案(转义 " 或 {})都不完美,因此在这种情况下我会选择简单的连接操作,因为使用字符串插值或格式化只为了使用而没有任何好处。 - DAXaholic
我知道转义字符可能会很嘈杂和分散注意力,但如果你要转义,最好使用当前语言的本地规则。对于简单的字符串连接,你提出了一个很好的观点。我已经在我的答案中尝试总结了所有选项,请告诉我是否漏掉了什么。 - mklement0
1
没错,如果你原来使用的是双引号,发现它不起作用,然后改成单引号还是不行,最后开始把 { 替换成 {{,这可能有点傻。总之,总结得很好,给你一个赞 - 我刚刚加入了另一个可能的解决方案,使用 JSON cmdlets,虽然需要更多代码,但根本不需要转义。 - DAXaholic
感谢;同样,指向Convert*-Json cmdlets作为(在性能方面代价高昂,但在便利性和健壮性方面卓越)替代方案也值得一提。 - mklement0
我遇到了输入错误:“格式化字符串错误:输入字符串不符合正确的格式。” - Kitwradr

1
你可以使用一个“Here-string”。它的声明方式与普通字符串相同,只是两端有一个@符号。例如:
$jsonfile = @"
  {
    "Version": "2012-10-17",
    "Statement": {
      "Effect": "Allow", 
      "Action": "sts:AssumeRole",
      "Resource": "arn:aws:iam::$variable:role/xxxxxx"}
  }
"@

然而,在这个特定的例子中,:role 部分会让 Powershell 有些困惑。所以你需要像这样转义冒号:$variable`:role 或者将变量包裹起来,像这样:$($variable):role 或者 ${variable}:role

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接