如何让PowerShell函数通过ValueFromRemainingArguments支持同名多参数
问题描述
为GitHub CLI编写了PowerShell包装函数Invoke-Gh,用于处理重复执行场景(比如GraphQL请求限流自动重试)。函数通过ValueFromRemainingArguments接收剩余参数,多数调用场景正常,但调用GraphQL API传入多个大小写不同的同名参数(如-F enterprise=$enterprise、-f query='xxx')时,会报错:
Invoke-Gh: Cannot bind parameter because parameter 'f' is specified more than once...
可执行invoke-gh -f -F直接复现该问题。
函数代码如下:
function Invoke-Gh { <# .Synopsis Wrapper function that deals with Powershell's peculiar error output when gh uses the error stream. .Example Invoke-Git ThrowError $LASTEXITCODE #> [CmdletBinding()] param( [Parameter(Mandatory=$false)] [switch]$slurp, [Parameter(Mandatory=$false)] [switch]$fromJson, [parameter(ValueFromRemainingArguments=$true)] [string[]]$Arguments ) $output = & { [CmdletBinding()] param( [parameter(ValueFromRemainingArguments=$true)] [string[]]$InnerArgs ) $sleep = 0 do { $output = & gh $InnerArgs if ($sleep -gt 0) { Start-Sleep -Seconds $sleep } $sleep += 5 } while ($output -like "*GraphQL: was submitted too quickly*") if ($LASTEXITCODE -ne 0) { throw "Error executing gh command: $InnerArgs" } if ($slurp) { $output = $output | & jq -s } if ($fromJson) { $output = $output | ConvertFrom-Json } return $output } -ErrorAction SilentlyContinue -ErrorVariable fail @Arguments if ($fail) { $fail.Exception } return $output }
目标调用示例:
&gh api graphql --paginate -F enterprise=$enterprise -f query='query($endCursor: String, $enterprise: String!) { enterprise(slug: $enterprise) { id, organizations(first: 100, after: $endCursor) { nodes { login, name }, pageInfo { hasNextPage endCursor } } } }' --jq '.data.enterprise.organizations.nodes'
解决方案
问题根源是PowerShell参数绑定不区分大小写,会把-F和-f识别为同一个参数名,而内部脚本块的参数绑定逻辑不允许重复参数。解决方法是取消内部脚本块的参数定义,直接使用自动变量$args接收所有传递的参数,绕过PowerShell的参数解析逻辑,让参数原样传递给gh命令。
修改后的函数代码:
function Invoke-Gh { <# .Synopsis Wrapper function that deals with Powershell's peculiar error output when gh uses the error stream. .Example Invoke-Git ThrowError $LASTEXITCODE #> [CmdletBinding()] param( [Parameter(Mandatory=$false)] [switch]$slurp, [Parameter(Mandatory=$false)] [switch]$fromJson, [parameter(ValueFromRemainingArguments=$true)] [string[]]$Arguments ) $output = & { $sleep = 0 do { $output = & gh $args if ($sleep -gt 0) { Start-Sleep -Seconds $sleep } $sleep += 5 } while ($output -like "*GraphQL: was submitted too quickly*") if ($LASTEXITCODE -ne 0) { throw "Error executing gh command: $args" } if ($slurp) { $output = $output | & jq -s } if ($fromJson) { $output = $output | ConvertFrom-Json } return $output } -ErrorAction SilentlyContinue -ErrorVariable fail @Arguments if ($fail) { $fail.Exception } return $output }
修改说明
- 移除内部脚本块的
[CmdletBinding()]属性和param定义,避免PowerShell对传入参数进行绑定解析 - 将内部的
$InnerArgs替换为自动变量$args,该变量会自动收集所有传递给脚本块的参数 - 无论参数大小写是否不同,都会原样传递给
gh命令,不会触发重复参数绑定错误
修改后即可正常执行目标调用:
Invoke-Gh api graphql --paginate -F enterprise=$enterprise -f query='query($endCursor: String, $enterprise: String!) { enterprise(slug: $enterprise) { id, organizations(first: 100, after: $endCursor) { nodes { login, name }, pageInfo { hasNextPage endCursor } } } }' --jq '.data.enterprise.organizations.nodes'
内容的提问来源于stack exchange,提问作者jessehouwing
相关产品推荐
相关产品推荐

