如何在PowerShell字符串中延迟变量的扩展?

11
无论你想如何称呼它,我正在尝试找到一种方法,将现有字符串的内容作为双引号字符串进行评估。例如,如果我创建以下字符串:
$string = 'The $animal says "meow"'
$animal = 'cat'

然后,Write-Host $string 将会输出 The $animal says "meow"。我该如何重新评估 $string,以输出(或分配给一个新的变量)The cat says "meow"

很烦人...评论的限制使得包含反引号代码非常困难(如果可能的话)。这里是我对zdan下面最后两条评论的未篡改版本:

----------

实际上,经过思考,我意识到期望The $animal says "meow"在插值时不转义双引号是不合理的,因为如果一开始就是双引号字符串,如果不转义双引号,评估将会中断。所以我想答案应该是一个两步过程:

$newstring = $string -replace '"', '`"'
iex "`"$string`""

最后一句话留给后人:我尝试过将所有内容放在一行上,几乎任何你认为可行的方法都会在输入iex之后出现问题,但是以下方法可以实现:

iex ('"' + ($string -replace '"', '`"') + '"')
5个回答

11

可能最简单的方法是

$ExecutionContext.InvokeCommand.ExpandString($var)

谢谢,那 确实 很有用,虽然我不一定会说它更简单。我试过了,除非加上转义字符,否则双引号会被去掉,所以在每个双引号前面添加一个反引号的额外步骤仍然是必要的。 $ExecutionContext.InvokeCommand.ExpandString($string) 返回 The cat says meow,而不是 The cat says "meow",除非双引号被转义。 - Adi Inbar
这个函数在PS3上有各种陷阱,强烈建议避免使用,特别是如果你需要支持PS2和PS3(行为不同的地方)。 - jpmc26
1
我最终编写了一个Expand-String cmdlet来处理版本差异。它在这里的Github上。 - alx9r

8
您可以使用Invoke-Expression来重新解析字符串 - 就像这样:
$string = 'The $animal says `"meow`"'
$animal = 'cat'
Invoke-Expression "Write-Host `"$string`""

请注意,在字符串内部使用反引号转义双引号以避免混淆解析器。这包括原始字符串中的任何双引号。
另外,请注意第一个命令应该是一个命令,如果需要使用生成的字符串,只需使用write-output将输出导出并将其分配给稍后可以使用的变量即可。
$result = Invoke-Expression "write-output `"$string`""

正如您在评论中所指出的那样,如果无法修改字符串的创建以转义双引号,则必须自己完成此操作。您还可以将其包装在函数中,以使其看起来更清晰:

function Invoke-String($str) { 
    $escapedString =  $str -replace '"', '`"'
    Invoke-Expression "Write-Output `"$escapedString`""
}

现在看起来会是这样的:

# ~> $string = 'The $animal says "meow"'
# ~> $animal = 'cat'
# ~> Invoke-String $string
The cat says "meow"

我尝试使用iex进行实验,但遇到了两个问题。第一个问题是,如果$string是一个命令,iex会尝试调用它。你在这里建议的方法是将其放在转义的内部双引号中(iex ""$string""而不是iex "$string"),可以解决这个问题。事实上,iex '"$string"'也可以工作。然而... - Adi Inbar
第二个问题是,如果$string包含双引号,它将会出现问题。实际上,这就是我使用包含双引号的字符串作为示例的原因。修改$string以使其起作用会破坏它的目的。我不想让示例过于复杂,因为我希望得到一般性的答案,而不是解决特定任务的变通方法,但$string并不是为了后续重新评估而存在。它需要成为任何它本来的东西,而不是为了适应特定的解决方案而量身定制。 - Adi Inbar
另外需要注意的是,在我遇到这个问题的函数中,$string本身是一个稍后由iex调用的命令。该函数构建了包含变量和双引号文本的命令行,然后使用iex $string调用它。我还希望它显示被调用的完整命令,而不必为此重新构建命令。如果我转义双引号,那么iex $string将无法工作。(我没有提供函数,因为我对一般适用的解决方案感兴趣,而不是针对这个特定情况的解决方法)。 - Adi Inbar
1
不需要使用write-hostwrite-output,只需使用Invoke-Expression "\"$string`""`即可。 - Chris F Carroll
1
这个答案需要一个巨大的霓虹警告,提醒人们不要将任何用户输入作为代码来调用。 - jpmc26
显示剩余4条评论

4
你可以使用-f运算符。据我所知,这与调用[String]::Format相同。
PS C:\> $string = 'The {0} says "meow"'
PS C:\> $animal = 'cat'
PS C:\> Write-Host ($string -f $animal)
The cat says "meow"

这样可以避免引号剥离(ExpandStringInvoke-Expression面临的问题)以及任意代码执行(Invoke-Expression面临的问题)。我已经测试过它在版本2及以上是支持的;我不完全确定它是否存在于PowerShell 1中。


0
Invoke-Expression "`"$string`""

但是引号仍然很烦人。Invoke-Expression "\"$($string -replace '"', '``"')`""` 处理引号,但不处理反斜杠引号。 - Chris F Carroll

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