使用扩展字符串作为 Powershell 函数参数
Using expanding strings as Powershell function parameters
我正在尝试编写一个函数来打印用户提供的问候语,地址是用户提供的名称。我想在此代码块中尽可能地使用扩展字符串:
$Name = "World"
$Greeting = "Hello, $Name!"
$Greeting
成功打印 Hello, World!
。但是,当我尝试将这些字符串作为参数传递给这样的函数时,
function HelloWorld
{
Param ($Greeting, $Name)
$Greeting
}
HelloWorld("Hello, $Name!", "World")
我得到了输出
Hello, !
World
经调查,Powershell 似乎完全忽略了 "Hello, $Name!"
中的 $Name
,因为 运行
HelloWorld("Hello, !", "World")
产生与上面相同的输出。此外,它似乎没有将 "World"
视为 $Name
的值,因为 运行
function HelloWorld
{
Param ($Greeting, $Name)
$Name
}
HelloWorld("Hello, $Name!", "World")
不产生任何输出。
有没有办法让扩展字符串在作为函数参数传入时起作用?
为了延迟字符串插值并按需执行,用then-current值,您必须 在 单引号 字符串上使用 $ExecutionContext.InvokeCommand.ExpandString()
[1] 作为 模板:
function HelloWorld
{
Param ($Greeting, $Name)
$ExecutionContext.InvokeCommand.ExpandString($Greeting)
}
HelloWorld 'Hello, $Name!' 'World' # -> 'Hello, World!'
请注意 'Hello, $Name!'
如何被 单引号 引用以防止即时扩展(插值)。
还要注意 HelloWorld
是如何被调用的,它的参数用 spaces 分隔,而不是 ,
,并且没有 (...)
].
在 PowerShell 中,函数的调用类似于命令行可执行文件 - foo arg1 arg2
- 不像 C# 方法 - foo(arg1, arg2)
- 见 Get-Help about_Parsing
.
如果您不小心使用 ,
来分隔您的参数,您将构造一个 array 函数将其视为 single 参数。
为帮助您避免意外使用方法语法,您可以使用 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!'
警告:PowerShell 字符串插值支持完整命令 - 例如,"Today is $(Get-Date)"
- 所以 除非您完全控制或信任模板字符串,此技术可能存在 安全风险。
Ansgar Wiechers proposes a safe alternative based on .NET string formatting 通过 PowerShell 的 -f
运算符 和 索引占位符 ({0}
, {1}
, . ..):
请注意,您不能再将 转换 作为模板字符串的一部分应用到参数上,也不能在其中嵌入命令。
function HelloWorld
{
Param ($Greeting, $Name)
$Greeting -f $Name
}
HelloWorld 'Hello, {0}!' 'World' # -> 'Hello, World!'
陷阱:
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
PowerShell 的字符串扩展支持扩展 collections(数组)——它将它们扩展为 space 分隔的列表——而 -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[]'
[1] 以更易于发现的方式显示 $ExecutionContext.InvokeCommand.ExpandString()
方法 的功能,即通过 Expand-String
cmdlet,是GitHub feature-request issue #11693的主题。
出现您的问题是因为 $Name
字符串替换发生在函数外部,在函数内部填充 $Name
变量之前。
您可以改为这样做:
function HelloWorld
{
Param ($Greeting, $Name)
$Greeting -replace '$Name',$Name
}
HelloWorld -Greeting 'Hello, $Name!' -Name 'World'
通过使用单引号,我们发送 Hello, $Name
的文字问候语,然后使用 -Replace
在函数内替换这个字符串(我们必须放一个 \
在我们要替换的字符串中的 $
之前,因为 $
是一个正则表达式特殊字符)。
我正在尝试编写一个函数来打印用户提供的问候语,地址是用户提供的名称。我想在此代码块中尽可能地使用扩展字符串:
$Name = "World"
$Greeting = "Hello, $Name!"
$Greeting
成功打印 Hello, World!
。但是,当我尝试将这些字符串作为参数传递给这样的函数时,
function HelloWorld
{
Param ($Greeting, $Name)
$Greeting
}
HelloWorld("Hello, $Name!", "World")
我得到了输出
Hello, !
World
经调查,Powershell 似乎完全忽略了 "Hello, $Name!"
中的 $Name
,因为 运行
HelloWorld("Hello, !", "World")
产生与上面相同的输出。此外,它似乎没有将 "World"
视为 $Name
的值,因为 运行
function HelloWorld
{
Param ($Greeting, $Name)
$Name
}
HelloWorld("Hello, $Name!", "World")
不产生任何输出。
有没有办法让扩展字符串在作为函数参数传入时起作用?
为了延迟字符串插值并按需执行,用then-current值,您必须 在 单引号 字符串上使用 $ExecutionContext.InvokeCommand.ExpandString()
[1] 作为 模板:
function HelloWorld
{
Param ($Greeting, $Name)
$ExecutionContext.InvokeCommand.ExpandString($Greeting)
}
HelloWorld 'Hello, $Name!' 'World' # -> 'Hello, World!'
请注意 'Hello, $Name!'
如何被 单引号 引用以防止即时扩展(插值)。
还要注意 HelloWorld
是如何被调用的,它的参数用 spaces 分隔,而不是 ,
,并且没有 (...)
].
在 PowerShell 中,函数的调用类似于命令行可执行文件 - foo arg1 arg2
- 不像 C# 方法 - foo(arg1, arg2)
- 见 Get-Help about_Parsing
.
如果您不小心使用 ,
来分隔您的参数,您将构造一个 array 函数将其视为 single 参数。
为帮助您避免意外使用方法语法,您可以使用 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!'
警告:PowerShell 字符串插值支持完整命令 - 例如,"Today is $(Get-Date)"
- 所以 除非您完全控制或信任模板字符串,此技术可能存在 安全风险。
Ansgar Wiechers proposes a safe alternative based on .NET string formatting 通过 PowerShell 的 -f
运算符 和 索引占位符 ({0}
, {1}
, . ..):
请注意,您不能再将 转换 作为模板字符串的一部分应用到参数上,也不能在其中嵌入命令。
function HelloWorld
{
Param ($Greeting, $Name)
$Greeting -f $Name
}
HelloWorld 'Hello, {0}!' 'World' # -> 'Hello, World!'
陷阱:
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
PowerShell 的字符串扩展支持扩展 collections(数组)——它将它们扩展为 space 分隔的列表——而
-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[]'
[1] 以更易于发现的方式显示 $ExecutionContext.InvokeCommand.ExpandString()
方法 的功能,即通过 Expand-String
cmdlet,是GitHub feature-request issue #11693的主题。
出现您的问题是因为 $Name
字符串替换发生在函数外部,在函数内部填充 $Name
变量之前。
您可以改为这样做:
function HelloWorld
{
Param ($Greeting, $Name)
$Greeting -replace '$Name',$Name
}
HelloWorld -Greeting 'Hello, $Name!' -Name 'World'
通过使用单引号,我们发送 Hello, $Name
的文字问候语,然后使用 -Replace
在函数内替换这个字符串(我们必须放一个 \
在我们要替换的字符串中的 $
之前,因为 $
是一个正则表达式特殊字符)。