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

时间:2013-03-01 23:36:45

标签: string powershell interpolation variable-expansion

无论你想要什么,我都试图找出一种方法来获取现有字符串的内容并将它们评估为双引号字符串。例如,如果我创建以下字符串:

$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 个答案:

答案 0 :(得分:7)

可能最简单的方法是

$ExecutionContext.InvokeCommand.ExpandString($var)

答案 1 :(得分:6)

您可以使用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"

答案 2 :(得分:3)

您可以使用-f operator。这与我可以确定的调用[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中。

答案 3 :(得分:2)

编辑:事实证明,字符串插值行为因PowerShell版本而异。我编写了一个更好的xsExpand-String)cmdlet版本,其中包含单元测试以处理该行为over here on GitHub


此解决方案的灵感来自this answer about shortening calls to object methods while retaining context。您可以将以下函数放在某个实用程序模块中,当您从另一个模块调用它时它仍然有效:

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)
    }
}

然后当你需要做延迟变量扩展时,你可以这样使用它:

$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并延迟扩展,直到变量具有您想要的值。

答案 4 :(得分:0)

Invoke-Expression "`"$string`""