You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写支持管道与多输入的高级PowerShell函数?

问题描述

摘要

如何编写支持管道的高级PowerShell函数,实现以下功能?

<#
Test-One接受[T]并返回[U]
Test-Two接受[U]并返回[V]
[T]、[U]和[V]可能被管道枚举,需避免这种情况
#>

<#
无管道,单输入
接受[T]并返回[V]
#>
$input = ...
$cache = Test-One -Input $input
$output = Test-Two -Input $cache

<#
带管道,单输入
接受[T]并返回[V]
#>
$input = ...
$output = $input | Test-One | Test-Two

<#
带管道,多输入
通过接受[T]并返回[V]的管道,接受[T[]]并返回[V[]]
#>
$input = @(..., ...)
$output = $input | Test-One | Test-Two

<#
无管道,多输入
通过直接返回中间的[U[]],接受[T[]]并返回[V[]]
#>
$input = @(..., ...)
$cache = Test-One -Input $input
$output = Test-Two -Input $cache

完整问题

我正在编写一个PowerShell模块,已完成基础逻辑实现,当前包含两个函数:

Function Get-HelpCommentTokens
{
    Param
    (
        [Parameter(Mandatory)]
        [string]
        $Command
    )
    # 逻辑
    # 返回[Token[]]
}

Function Get-TextFromCommentTokens
{
    Param
    (
        [Parameter(Mandatory)]
        [Token[]]
        $Tokens
    )
    # 逻辑
    # 返回[string[]]
}

当前用法:

# 无管道,单输入
$HelpTokens = Get-HelpCommentTokens -Command "help"
$HelpText = Get-TextFromCommentTokens -Tokens $HelpTokens

我现在要为这两个函数添加管道和多输入支持,目标用法如下:

# 带管道,单输入
$HelpTokens =  "help" | Get-HelpCommentTokens | Get-TextFromCommentTokens # 应接受[string]并返回[string[]]
# 带管道,多输入
$HelpTokens =  "help", "other" | Get-HelpCommentTokens | Get-TextFromCommentTokens # 应接受[string[]]并返回[string[][]]
# 无管道,多输入
$HelpTokens = Get-HelpCommentTokens -Command "help", "other" # 应接受[string[]]并返回[Token[][]]
$HelpText = Get-TextFromCommentTokens -Tokens $HelpTokens # 应接受[Token[][]]并返回[string[][]]

对于「带管道,单输入」和「带管道,多输入」场景,PowerShell内置管道引擎可很好处理,但「无管道,多输入」场景存在问题:若直接将参数设为数组,PowerShell管道引擎会扁平化输出并转换为单元素数组(例如@(@(1, 2), @(3, 4)) -> @(@(1), @(2), @(3), @(4))),因此以下方案不可行:

Function Get-HelpCommentTokens
{
    Param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [string[]]
        $Command
    )
    # 遍历逻辑
    # 返回[Token[][]]
}

Function Get-TextFromCommentTokens
{
    Param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [Token[][]]
        $Tokens
    )
    # 遍历逻辑
    # 返回[string[][]]
}

另一种方案是为管道单独设置参数集,但我希望管道与非管道输入使用相同参数名,请问有什么可行的思路或解决方案?


解决方案

核心思路:区分管道输入与非管道输入的处理逻辑

利用PowerShell的$MyInvocation.ExpectingInput属性判断输入方式,结合单值参数类型避免数组扁平化问题,同时保持参数名一致。

改造后的Get-HelpCommentTokens函数

Function Get-HelpCommentTokens
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [string]
        $Command
    )

    Begin {
        # 初始化集合存储非管道输入的结果
        $results = @()
    }

    Process {
        # 执行单条Command的核心逻辑,返回[Token[]]
        $tokens = # 替换为你的原有处理逻辑
        if ($MyInvocation.ExpectingInput) {
            # 管道输入:直接输出单条结果,由管道引擎自动收集
            $tokens
        }
        else {
            # 非管道输入:用逗号前缀保留数组结构,避免扁平化
            $results += ,$tokens
        }
    }

    End {
        if (-not $MyInvocation.ExpectingInput) {
            # 非管道输入时输出完整结果集合
            $results
        }
    }
}

改造后的Get-TextFromCommentTokens函数

Function Get-TextFromCommentTokens
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [Token[]]
        $Tokens
    )

    Begin {
        $results = @()
    }

    Process {
        # 执行单条Token数组的核心逻辑,返回[string[]]
        $text = # 替换为你的原有处理逻辑
        if ($MyInvocation.ExpectingInput) {
            $text
        }
        else {
            $results += ,$text
        }
    }

    End {
        if (-not $MyInvocation.ExpectingInput) {
            $results
        }
    }
}

方案说明

  1. 单值参数类型:管道输入时,PowerShell会自动遍历输入数组的每个元素,逐个传入Process块;非管道输入时,传入的多值参数也会被拆分为单个元素依次处理,避免直接使用数组参数导致的扁平化。
  2. $MyInvocation.ExpectingInput判断:
    • 管道输入时该属性为$true,直接输出单条结果,管道引擎会自动将所有结果收集为数组。
    • 非管道输入时该属性为$false,用,$tokens的方式将结果加入集合,强制保留数组的二维结构,避免PowerShell自动扁平化。
  3. 兼容所有目标用法:
    • 管道单输入:返回[string[]]
    • 管道多输入:返回[string[][]]
    • 非管道多输入:Get-HelpCommentTokens返回[Token[][]],传入Get-TextFromCommentTokens后得到[string[][]]

替代方案:使用泛型列表存储结果

如果希望更直观地管理集合,可使用泛型列表替代普通数组,避免扁平化问题:

Function Get-HelpCommentTokens
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [string]
        $Command
    )

    Begin {
        $results = [System.Collections.Generic.List[Token[]]]::new()
    }

    Process {
        $tokens = # 替换为你的原有处理逻辑
        if ($MyInvocation.ExpectingInput) {
            $tokens
        }
        else {
            $results.Add($tokens)
        }
    }

    End {
        if (-not $MyInvocation.ExpectingInput) {
            $results
        }
    }
}

内容的提问来源于stack exchange,提问作者ZP-ZPanda

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 11:37:22