如何在Powershell中使用破折号参数?

问题描述 投票:0回答:3

我正在将脚本从 bash 移植到 PowerShell,并且我希望在两者中保持对参数解析的相同支持。在 bash 中,可能的参数之一是

--
,我还想在 PowerShell 中检测该参数。然而,到目前为止我所尝试的一切都没有奏效。我无法将其定义为像
param($-)
这样的参数,因为这会导致编译错误。另外,如果我决定完全放弃 PowerShell 参数处理,而只使用
$args
一切看起来都很好,但是当我运行该函数时,
--
参数丢失了。

Function Test-Function {
    Write-Host $args
}

Test-Function -- -args go -here # Prints "-args go -here"

我也知道

$PSBoundParameters
,但该值不存在,因为我无法绑定名为
$-
的参数。这里还有我可以尝试的其他机制或任何解决方案吗?

为了了解更多背景信息,请注意我使用 PowerShell 是有副作用的。这预计不会用作普通的 PowerShell 命令,我还围绕此编写了一个批处理包装器,但包装器的逻辑比我想要批量编写的更复杂,因此批处理包装器仅调用 PowerShell 函数,然后进行更复杂的处理。

powershell parameter-passing
3个回答
4
投票

我找到了一种方法来做到这一点,但你必须传递其中的 3 个连字符,而不是双连字符。

这是一个简单的函数,您可以根据需要更改代码:

function Test-Hyphen {
    param(
        ${-}
    )
    if (${-}) {
        write-host "You used triple-hyphen"
    } else {
        write-host "You didn't use triple-hyphen"
    }
}

样品1

Test-Hyphen

输出

You didn't use triple-hyphen

样品2

Test-Hyphen ---

输出

You used triple-hyphen


3
投票

顺便说一句:PowerShell 允许使用令人惊讶的变量名称范围,但您必须将它们括在

{...}
中才能识别它们;也就是说,
${-}
在技术上是可行的,但它并不能解决你的问题。

挑战在于,PowerShell 会悄悄地从参数列表中删除

--,而保留该标记的唯一方法是在其前面添加 PSv3+ 停止解析符号,--%
,然而,这
从根本上改变了参数的传递方式,并且显然是一个额外的要求,这正是您想要避免的。

你最好的选择是尝试 - 次优 -

解决方法

  • 选项 A:在批处理文件包装器中,将

    --

     转换为 PowerShell 
    保留并传递的特殊参数;然后,PowerShell 脚本必须将该特殊参数重新翻译为 --

  • 选项 B:在 PowerShell 中执行自定义参数解析:

您可以分析

$MyInvocation.Line

[Environment]::CommandLine
(其中包含调用脚本的原始命令行),并查找其中是否存在 
--

然而,正确实现这一点并使其稳健并非易事。

这是一个
合理稳健的方法:

# Don't use `param()` or `$args` - instead, do your own argument parsing: if ($MyInvocation.Line) { # In-session invocation or CLI call with -Command (-c) $ownName = $MyInvocation.InvocationName $invocationLine = $MyInvocation.Line } else { # CLI call with -File (-f) $ownName = Split-Path -Leaf $PSCommandPath $invocationLine = [Environment]::CommandLine } # Extract the argument list from the invocation command line. $argList = ($invocationLine -replace ('^.*?' + [regex]::Escape($ownName)) -split '[;|]')[0].Trim() # Use Invoke-Expression with a Write-Output call to parse the raw argument list, # performing evaluation and splitting it into an array: $customArgs = if ($argList) { @(Invoke-Expression "Write-Output -- $argList") } else { @() } # Print the resulting arguments array for verification: $i = 0 $customArgs | % { "Arg #$((++$i)): [$_]" }
注:

  • 毫无疑问存在一些边缘情况,其中参数列表可能无法正确提取,或者原始参数的重新评估会导致副作用,但对于大多数情况 - 特别是从

    外部 PowerShell 调用时 - 应该这样做。

  • 虽然在这里很有用,但通常应避免

    Invoke-Expression

  • 如果您的脚本名为
foo.ps1

并且您将其调用为

./foo.ps1 -- -args go -here
,您将看到以下输出:
Arg #1: [--]
Arg #2: [-args]
Arg #3: [go]
Arg #4: [-here]



0
投票
$MyInfocation.Line

值的末尾),然后使用

Invoke-Expression
Write-Output
来获取实际的参数值:
# Parse the whole invocation line
$code = [System.Management.Automation.Language.Parser]::ParseInput($MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1), [ref]$null, [ref]$null)
# Find our invocation expression without redirections
$myline = $code.Find({$args[0].CommandElements}, $true).CommandElements | % { $_.ToString() } | Join-String -Separator ' '
# Get the argument values
$command, $arguments = Invoke-Expression ('Write-Output -- ' + $myline)
# Fine-tune arguments to be always an array
if ( $arguments -is [string] ) { $arguments = @($arguments) }
if ( $arguments -eq $null ) { $arguments = @() }

请注意,函数调用中的原始值会在 
Invoke-Expression

中重新计算,因此任何局部变量可能会隐藏实际参数的值。因此,您还可以在函数顶部使用这个(几乎)单行代码,这可以防止局部变量的污染:

# Parse arguments
$command, $arguments = Invoke-Expression ('Write-Output -- ' + ([System.Management.Automation.Language.Parser]::ParseInput($MyInvocation.Line.Substring($MyInvocation.OffsetInLine - 1), [ref]$null, [ref]$null).Find({$args[0].CommandElements}, $true).CommandElements | % { $_.ToString() } | Join-String -Separator ' '))
# Fine-tune arguments to be always an array
if ( $arguments -is [string] ) { $arguments = @($arguments) }
if ( $arguments -eq $null ) { $arguments = @() }

	
© www.soinside.com 2019 - 2024. All rights reserved.