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

String 如何延迟PowerShell字符串中变量的扩展?,string,powershell,interpolation,variable-expansion,String,Powershell,Interpolation,Variable Expansion,不管你想叫它什么,我都在试图找到一种方法,将现有字符串的内容作为双引号字符串进行计算。例如,如果我创建以下字符串: $string = 'The $animal says "meow"' $animal = 'cat' 然后,Write Host$string将产生这个$animal说“喵”。如何重新计算$string以输出(或分配给新变量)猫说“喵” 多么烦人……注释上的限制使得包含带有反勾号的代码非常困难(如果可能的话)。下面是我在回应zdan时最后两条评论的一个不完整版本: -----

不管你想叫它什么,我都在试图找到一种方法,将现有字符串的内容作为双引号字符串进行计算。例如,如果我创建以下字符串:

$string = 'The $animal says "meow"'
$animal = 'cat'
然后,
Write Host$string
将产生
这个$animal说“喵”
。如何重新计算$string以输出(或分配给新变量)
猫说“喵”


多么烦人……注释上的限制使得包含带有反勾号的代码非常困难(如果可能的话)。下面是我在回应zdan时最后两条评论的一个不完整版本:

---------- 事实上,经过思考后,我意识到,在不转义双引号的情况下对$animal说“喵”的
进行插值是不合理的,因为如果它是一个双引号字符串,如果不转义双引号,计算就会中断。所以我想答案是这是一个两步的过程:

$newstring = $string -replace '"', '`"'
iex "`"$string`""
留给后代的最后一点意见是:我尝试了将所有这些都放在一条线上的方法,几乎任何你认为有效的东西都会在你将其提供给iex后中断,但这一条有效:

iex ('"' + ($string -replace '"', '`"') + '"')

您可以使用
Invoke Expression
对字符串进行重新分析,如下所示:

$string = 'The $animal says `"meow`"'
$animal = 'cat'
Invoke-Expression "Write-Host `"$string`""
# ~> $string = 'The $animal says "meow"'
# ~> $animal = 'cat'
# ~> Invoke-String $string
The cat says "meow"
$MyString = 'The $animal says $sound.'
...
$animal = 'fox'
...
$sound = 'simper'

&($MyString | xs)
&(xs $MyString)

PS> The fox says simper.
PS> The fox says simper.
注意您必须如何转义字符串中的双引号(使用反勾号),以避免混淆解析器。这包括原始字符串中的任何双引号

还要注意的是,第一个命令应该是一个命令,如果需要使用结果字符串,只需使用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-Expression "Write-Host `"$string`""
# ~> $string = 'The $animal says "meow"'
# ~> $animal = 'cat'
# ~> Invoke-String $string
The cat says "meow"
$MyString = 'The $animal says $sound.'
...
$animal = 'fox'
...
$sound = 'simper'

&($MyString | xs)
&(xs $MyString)

PS> The fox says simper.
PS> The fox says simper.

也许最简单的方法是

$ExecutionContext.InvokeCommand.ExpandString($var)
编辑:事实证明,字符串插值行为因PowerShell的版本而异。我编写了一个更好的
xs
Expand-String
)cmdlet版本,其中包含处理该行为的单元测试


此解决方案的灵感来自。您可以将以下函数放在实用程序模块中的某个位置,当您从另一个模块调用它时,它仍然有效:

function xs
{
    [CmdletBinding()]
    param
    (

        # The string containing variables that will be expanded.
        [parameter(ValueFromPipeline=$true,
                   Position=0,
                   Mandatory=$true)]
        [string]
        $String
    )
    process
    {
        $escapedString = $String -replace '"','`"'
        $code = "`$ExecutionContext.InvokeCommand.ExpandString(`"$escapedString`")"
        [scriptblock]::create($code)
    }
}
然后,当您需要执行延迟变量展开时,您可以这样使用它:

$string = 'The $animal says `"meow`"'
$animal = 'cat'
Invoke-Expression "Write-Host `"$string`""
# ~> $string = 'The $animal says "meow"'
# ~> $animal = 'cat'
# ~> Invoke-String $string
The cat says "meow"
$MyString = 'The $animal says $sound.'
...
$animal = 'fox'
...
$sound = 'simper'

&($MyString | xs)
&(xs $MyString)

PS> The fox says simper.
PS> The fox says simper.
$animal
$sound
直到最后两行才展开。这允许您预先设置
$MyString
,并延迟扩展,直到变量具有所需的值。

您可以使用。据我所知,这和打电话是一样的

PS C:\> $string = 'The {0} says "meow"'
PS C:\> $animal = 'cat'
PS C:\> Write-Host ($string -f $animal)
The cat says "meow"
这避免了与引号剥离(面临
ExpandString
Invoke Expression
)和任意代码执行(面临
Invoke Expression
)相关的陷阱


我已经测试过它在版本2和更高版本中受支持;我不能完全确定它是否存在于PowerShell 1中。

我尝试使用iex,但遇到了两个问题。第一个是,如果$string是一个命令,iex会尝试调用它。您在这里提出的建议,将其放在转义的内部双引号中(
iex“
”$string
,而不是
iex“$string”
),可以解决这个问题。事实上,
iex'$string'
也可以工作。但是……第二个问题是,如果$string包含双引号,它将中断。事实上,这就是我使用包含双引号的字符串作为示例的原因。修改$string以使其正常工作违背了此目的。我不想让这个例子过于复杂,因为我想要一个通用的答案,而不是完成一个特定任务的变通方法,但是$string的存在并不是为了以后重新评估。它需要是任何东西,而不是为适应特定的解决方案而定制。另一个注意事项:在我遇到这个问题的函数中,$string本身是一个后来由iex调用的命令。该函数构建包含变量和双引号文本的命令行,然后使用
iex$string
调用它。我还想让它显示正在调用的完整命令,而不必为此重新生成命令。如果我转义双引号,那么
iex$string
将不起作用。(我没有提供该函数,因为我感兴趣的是一个普遍适用的解决方案,而不是针对这一特定情况的变通办法)。既不需要
写入主机
也不需要
写入输出
,只需
调用表达式“`$string`”
行吗?这个答案需要一个巨大的霓虹灯警告,警告将任何用户输入作为代码调用的危险。谢谢,这很有用,尽管我不一定说它更简单。我试过了,双引号被去掉了,除非它们被转义,所以在每个双引号前面添加一个反勾号的额外步骤仍然是必要的
$ExecutionContext.InvokeCommand.ExpandString($string)
返回
猫说喵喵
,而不是
猫说“喵喵”
,除非双引号被转义。从PS3开始,此函数有各种陷阱。强烈建议避免,特别是当您需要支持PS2和PS3(行为不同)时。我最后编写了一个
Expand-String
cmdlet来处理版本差异。是的,但是引语仍然让人恼火<代码>调用表达式“`”$($string-replace',“``”)```
处理引号,但不处理反勾引号
Invoke-Expression "`"$string`""