【问题标题】:How do I delay expansion of variables in PowerShell strings?如何延迟 PowerShell 字符串中变量的扩展?
【发布时间】:2019-03-29 16:59:12
【问题描述】:

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

$string = 'The $animal says "meow"'
$animal = 'cat'

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


多么烦人...... cmets 的限制使得包含带有反引号的代码变得非常困难(如果可能的话)。下面是我为响应 zdan 而制作的最后两个 cmets 的未修改版本:

---------

实际上,在考虑之后,我意识到期望在不转义双引号的情况下对The $animal says "meow" 进行插值是不合理的,因为如果它是一个双引号字符串开头,那么如果双引号,则评估会中断引号没有转义。所以我想答案是,这是一个两步过程:

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

给后人的最后一条评论:我尝试了将所有内容放在一条线上的方法,几乎​​所有你认为可行的东西在你将它提供给 iex 后都会中断,但这个可行:

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

【问题讨论】:

    标签: string powershell interpolation variable-expansion


    【解决方案1】:

    可能最简单的方法是

    $ExecutionContext.InvokeCommand.ExpandString($var)
    

    【讨论】:

    • 谢谢,这有用的,虽然我不一定会说它更简单。我试过了,双引号被去掉了,除非它们被转义,所以在每个双引号前面添加反引号的额外步骤仍然是必要的。 $ExecutionContext.InvokeCommand.ExpandString($string) 返回 The cat says meow,而不是 The cat says "meow",除非双引号被转义。
    • 这个功能从 PS3 开始就有各种陷阱。强烈建议避免使用,尤其是在您需要支持 PS2 和 PS3(行为不同)时。
    • 我最终编写了一个处理版本差异的Expand-String cmdlet。这是over here on github
    【解决方案2】:

    您可以使用 Invoke-Expression 重新解析您的字符串 - 如下所示:

    $string = 'The $animal says `"meow`"'
    $animal = 'cat'
    Invoke-Expression "Write-Host `"$string`""
    

    请注意您必须如何转义字符串中的双引号(使用反引号)以避免混淆解析器。这包括原始字符串中的任何双引号。

    另外请注意,第一个命令应该是一个命令,如果您需要使用生成的字符串,只需使用 write-output 管道输出并将其分配给您以后可以使用的变量:

    $result = Invoke-Expression "write-output `"$string`""
    

    如您的 cmets 中所述,如果您无法修改字符串的创建以转义双引号,则您必须自己执行此操作。您还可以将其包装在一个函数中以使其看起来更清晰:

    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"' 也可以。但是......
    • ...第二个问题是如果 $string 包含双引号,它会中断。事实上,这就是我使用包含双引号的字符串作为示例的原因。修改 $string 以使其正常工作会破坏目的。我不想使示例过于复杂,因为我想要一个笼统的答案,而不是完成一项特定任务的变通方法,但是 $string 不存在仅用于稍后重新评估的唯一目的。它必须是任何东西,而不是为适应特定的解决方案而量身定制的。
    • 另一个注意事项:在我遇到这个问题的函数中,$string 本身是一个命令,后来被 iex 调用。该函数构建包含变量和双引号文本的命令行,然后使用iex $string 调用它。我还想让它显示正在调用的完整命令,而不必为此目的重新构建命令。如果我转义双引号,那么iex $string 将不起作用。 (我没有提供该功能,因为我对普遍适用的解决方案感兴趣,而不是针对这一特定情况的解决方法)。
    • write-hostwrite-output 都不需要,只需 Invoke-Expression "`"$string`"" 就可以了吗?
    • 这个答案需要一个巨大的霓虹灯警告,警告将任何用户输入作为代码调用的危险。
    【解决方案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 中。

    【讨论】:

      【解决方案4】:

      编辑:事实证明,字符串插值行为因 PowerShell 版本而异。我编写了一个更好版本的 xs (Expand-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 并延迟扩展,直到变量具有您想要的值。

      【讨论】:

        【解决方案5】:
        Invoke-Expression "`"$string`""
        

        【讨论】:

        • 但是引用仍然很烦人。 Invoke-Expression "`"$($string -replace '"', '``"')`"" 处理引号但不处理反引号
        猜你喜欢
        • 1970-01-01
        • 2017-05-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多