管道到用户函数参数问题

Pipeline to user function parameter problems

出于某种原因,我的程序拒绝在这种情况下工作:

这是函数:

function Get-ChildItemCustom {
    Param(
        [Parameter(ValueFromPipeline=$true)]
        [System.IO.FileSystemInfo] $item,
        [Parameter(ValueFromPipeline=$false)]
        [string] $archive_path
    )
    Process {
        Get-ChildItem $item
    }
}

我希望能够像 Get-ChildItem 一样使用此功能:输入一个 [System.IO.FileSystemInfo] 对象,并获取所有子项(使用我未在此处包含的一些额外条件进行排序)作为输出。

我是这样调用函数的:

Get-ChildItem $parentfolder_path |
    Get-ChildItemCustom |
    Do-SomethingElse

错误 returns 说明 Get-ChildItem 的结果(可验证类型 [System.IO.FileSystemInfo])被视为字符串。

Cannot convert the "E:\Data\VHG-ITC-Test\New folder\archive" value of type "System.String" to type "System.IO.FileSystemInfo".

参数前面的类型并不总是存在。当 $item 没有明确的类型时,函数会误读输入(据说只将 Name 属性 作为输入):

Get-ChildItem : Cannot find path 'C:\Windows\system32\New folder' because it does not exist.

所以该函数似乎无法正确接受对象输入。我想不惜一切代价避免使用字符串,而只是移动对象。我是不是参数设置错了?我能做什么?

问题不完全在于你的函数,而是在于 Get-ChildItem 处理参数的方式(正如 Moerwald 在他的回答中已经怀疑的那样)。

当您使用 FileInfo 对象作为未命名参数调用 Get-ChildItem 时,该参数被传递给第一个位置参数 (-Path),它需要一个字符串数组作为输入,所以对象被转换为字符串。但是,在某些情况下,将 FileInfo 对象转换为字符串会扩展 FullName 属性,而在其他情况下,它只会扩展 Name 属性(我不能不过,请解释 PowerShell 如何决定何时选择哪个)。后者是你的情况。由于 Get-ChildItem 只看到一个名称,而不是完整路径,它正在寻找当前工作目录中的项目,但失败了。

有多种方法可以避免此问题,Moerwald 已经展示了其中一种方法。其他是:

  • 使用管道将 $item 传递给 Get-ChildItem:

    function Get-ChildItemCustom {
        Param(
            [Parameter(ValueFromPipeline=$true)]
            [IO.FileSystemInfo]$item,
    
            [Parameter(ValueFromPipeline=$false)]
            [string]$archive_path
        )
    
        Process {
            $item | Get-ChildItem
        }
    }
    
  • 通过按名称映射 属性 来传递完整路径:

    function Get-ChildItemCustom {
        Param(
            [Parameter(
                ValueFromPipeline=$true,
                ValueFromPipelineByPropertyName=$true
            )]
            [string[]]$FullName,
    
            [Parameter(ValueFromPipeline=$false)]
            [string]$archive_path
        )
    
        Process {
            Get-ChildItem $FullName
        }
    }
    

就个人而言,我更喜欢最后一种变体(通过 属性 名称传递)。