使用扩展字符串作为Powershell函数参数

vaz*_*yte 4 powershell string-interpolation

我正在尝试编写一个函数,该函数将打印用户提供的问候语,该问候语发送给用户提供的名称.我想在这个代码块中使用扩展字符串:

$Name = "World"  
$Greeting = "Hello, $Name!"
$Greeting
Run Code Online (Sandbox Code Playgroud)

哪个成功打印Hello, World!.但是,当我尝试将这些字符串作为参数传递给类似的函数时,

function HelloWorld
{  
    Param ($Greeting, $Name)
    $Greeting
}
HelloWorld("Hello, $Name!", "World")
Run Code Online (Sandbox Code Playgroud)

我得到了输出

Hello, !  
World  
Run Code Online (Sandbox Code Playgroud)

经调查,PowerShell的似乎忽略了$Name"Hello, $Name!"完全与运行

HelloWorld("Hello, !", "World")
Run Code Online (Sandbox Code Playgroud)

产生与上述相同的输出.此外,它似乎并不认为"World"$Name运行以来 的价值

function HelloWorld
{  
    Param ($Greeting, $Name)
    $Name
}
HelloWorld("Hello, $Name!", "World")
Run Code Online (Sandbox Code Playgroud)

不产生任何产出.

当作为函数参数传入时,有没有办法让扩展字符串工作?

mkl*_*nt0 5

为了延迟字符串插值并按需执行,使用当前,您必须使用充当模板$ExecutionContext.InvokeCommand.ExpandString()单引号字符串:

function HelloWorld
{  
    Param ($Greeting, $Name)
    $ExecutionContext.InvokeCommand.ExpandString($Greeting)
}

HelloWorld 'Hello, $Name!' 'World'   # -> 'Hello, World!'
Run Code Online (Sandbox Code Playgroud)

注意如何'Hello, $Name!'单一 -quoted防止瞬间膨胀(插值).

还要注意如何HelloWorld调用它的参数用空格分隔,不用,和不用(...).

在PowerShell中,功能调用相似的命令行可执行文件 - foo arg1 arg2-不喜欢C#方法 - foo(arg1, arg2)-见Get-Help about_Parsing.
如果你不小心使用,分离你的论点,你将构建一个阵列,一个功能视为一个单一的参数.
为了帮助您避免意外使用方法语法,您可以使用Set-StrictMode -Version 2或更高,但请注意,这需要进行额外的严格性检查.

请注意,由于PowerShell函数默认情况下也会看到在父作用域(所有祖先作用域)中定义的变量,因此您可以简单地定义模板在调用作用域中引用的任何变量,而不是声明各个参数,例如$Name:

function HelloWorld
{  
    Param ($Greeting) # Pass the template only.
    $ExecutionContext.InvokeCommand.ExpandString($Greeting)
}

$Name = 'World'  # Define the variable(s) used in the template.
HelloWorld 'Hello, $Name!'     # -> 'Hello, World!'
Run Code Online (Sandbox Code Playgroud)

警告:PowerShell字符串插值支持完整命令 - 例如,"Today is $(Get-Date)"- 因此,除非您完全控制或信任模板字符串,否则此技术可能存在安全风险.


安斯加尔Wiechers提出了一种安全的替代基于.NET字符串格式化通过PowerShell的-f操作索引的占位符({0},{1},...):

请注意,您通常不再对参数应用转换作为模板字符串的一部分或嵌入命令.

function HelloWorld
{  
    Param ($Greeting, $Name)
    $Greeting -f $Name
}

HelloWorld 'Hello, {0}!' 'World'     # -> 'Hello, World!'
Run Code Online (Sandbox Code Playgroud)

陷阱:

  • PowerShell的字符串扩展使用不变文化,而-f运算符执行文化敏感格式(代码片段需要PSv3 +):

    $prev = [cultureinfo]::CurrentCulture
    # Temporarily switch to culture with "," as the decimal mark
    [cultureinfo]::CurrentCulture = 'fr-FR' 
    
    # string expansion: culture-invariant: decimal mark is always "."
    $v=1.2; "$v";  # -> '1.2'
    
    # -f operator: culture-sensitive: decimal mark is now ","
    '{0}' -f $v    # -> '1,2'  
    
    [cultureinfo]::CurrentCulture = $prev
    
    Run Code Online (Sandbox Code Playgroud)
  • PowerShell的字符串扩展支持扩展集合(数组) - 它将它们扩展为以空格分隔的列表 - 而-f运算符仅支持标量(单个值):

    $arr = 'one', 'two'
    
    # string expansion: array is converted to space-separated list
    "$var" # -> 'one two'
    
    # -f operator: array elements are syntactically treated as separate values
    # so only the *first* element replaces {0}
    '{0}' -f $var  # -> 'one'
    # If you use a *nested* array to force treatment as a single array-argument,
    # you get a meaningless representation (.ToString() called on the array)
    '{0}' -f (, $var)  # -> 'System.Object[]'
    
    Run Code Online (Sandbox Code Playgroud)


Mar*_*agg 2

出现问题的原因是,在变量填充到函数$Name内部之前,字符串替换发生在函数外部。$Name

你可以这样做:

function HelloWorld
{  
    Param ($Greeting, $Name)
    $Greeting -replace '\$Name',$Name
}

HelloWorld -Greeting 'Hello, $Name!' -Name 'World'
Run Code Online (Sandbox Code Playgroud)

通过使用单引号,我们发送 in 的字面问候语Hello, $Name,然后在函数内替换该字符串-Replace(我们必须在要替换的字符串\之前放置 a ,因为它是正则表达式特殊字符)。$$