如何创建一个从管道和命令行接受多个参数类型的函数?

Pau*_*ore 11 powershell arguments argument-passing

我正在尝试编写一个带有多个参数的函数,这些函数可以来自命令行,也可以来自管道.参数可以是字符串或目录对象.这个想法是以下任何调用应该工作:

Test-VEnv '.\MyPath', '.\AnotherPath'
Test-VEnv (dir)
'MyPath', 'AnotherPath' | Test-VEnv
dir | Test-VEnv
Run Code Online (Sandbox Code Playgroud)

以下代码几乎可以工作:

function Test-VEnv {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true, Position=0,
            ValueFromPipeline=$True,
            ValueFromPipelineByPropertyName=$true)]
        [Alias('FullName')]
        [String[]]$Path
    )

    process {
        foreach ($P in $Path) {
            ...
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它处理来自管道和命令参数的字符串,并处理来自管道的目录对象(通过ValueFromPipelineByPropertyName和FullName别名).但它不会在命令行上处理目录对象,所以

dir | Where-Object { Test-VEnv $_ }
Run Code Online (Sandbox Code Playgroud)

失败,因为它将目录对象转换为字符串,使用Name属性而不是FullName,后续代码失败.

有谁能告诉我如何实现我想要的?

我知道即使我可以让它工作,它可能不是一个特别好的设计.但据我所知,这就是内置的Test-Path的工作方式,所以我想在发明自己之前尝试遵循标准行为......

And*_*ndi 9

由于您的参数类型是string在不使用管道时将文件系统信息对象强制转换为字符串{ Test-VEnv $_ }.如果你调用ToString()a System.IO.FileInfoSystem.IO.DirectoryInfoobject 的方法,你会看到这个.当您使用管道时,它会绑定fullname别名,为您提供完整路径.

您可以看到PowerShell正在使用什么来绑定输入对象Trace-Command.以下是如何使用它的示例:

trace-command -name parameterbinding -expression {(dir C:\)[0] | ? {Test-VEnv $_}} -pshost
Run Code Online (Sandbox Code Playgroud)

这是输出的重要部分:

BIND arg [PerfLogs] to parameter [Path]
    Executing DATA GENERATION metadata: [System.Management.Automation.ArgumentTypeConverterAttribute]
        result returned from DATA GENERATION: System.String[]
    COERCE arg to [System.String[]]
        Parameter and arg types the same, no coercion is needed.
    BIND arg [System.String[]] to param [Path] SUCCESSFUL
Run Code Online (Sandbox Code Playgroud)

Test-Path做同样的事情.看看这三个例子:

PS C:\Users\Andy> Test-Path (dir C:\)[0]
False
PS C:\Users\Andy> (dir C:\)[0] | Test-Path
True
PS C:\> Test-Path (dir C:\)[0]
True
Run Code Online (Sandbox Code Playgroud)
  1. 由于我的PWD不是,C:\我得到FALSE,因为DirectoryInfo对象被转换为string(ToString()),它只提供文件夹名称.这是因为没有使用管道.

  2. 由于使用了管道,因此它可以使用此参数绑定到PsPath:

    [Parameter(ParameterSetName='LiteralPath', Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
    [Alias('PSPath')]
    [string[]]
    ${LiteralPath},
    
    Run Code Online (Sandbox Code Playgroud)
  3. 由于目录包含文件夹名称所在的文件夹.

您可以尝试PsPath绑定的别名.这是Test-Path用途:

param (
    [Parameter(Mandatory=$true, Position=0,
        ValueFromPipeline=$True,
        ValueFromPipelineByPropertyName=$true)]
    [Alias('PsPath')]
    [String[]] $Path
)

process {
    foreach ($P in $Path) {
        Get-Item $p
    }
}
Run Code Online (Sandbox Code Playgroud)

一些测试:

Set-Location C:\
Write-Host 1
    Test-VEnv '.\Windows', '.\Program Files'
Write-Host 2
    Test-VEnv (dir)
Write-Host 3
    'Windows', 'Program Files' | Test-VEnv
Write-Host 4
    dir | Test-VEnv
Run Code Online (Sandbox Code Playgroud)

输出:

1
    Directory: C:\
Mode                LastWriteTime     Length Name                                                       
----                -------------     ------ ----                                                       
d----         3/14/2012   3:41 AM            Windows                                                    
d-r--         3/24/2012   7:46 PM            Program Files                                              

2
d----         2/18/2012   4:32 AM            PerfLogs                                                   
d-r--         3/24/2012   7:46 PM            Program Files                                              
d-r--         3/25/2012   4:49 PM            Program Files (x86)                                        
d----          3/9/2012   9:57 PM            Python27                                                   
d-r--          3/4/2012   8:11 PM            Users                                                      
d----         3/14/2012   3:41 AM            Windows                                                    
-a---          3/4/2012   8:45 PM       1024 .rnd                                                       

3
d----         3/14/2012   3:41 AM            Windows                                                    
d-r--         3/24/2012   7:46 PM            Program Files                                              

4
d----         2/18/2012   4:32 AM            PerfLogs                                                   
d-r--         3/24/2012   7:46 PM            Program Files                                              
d-r--         3/25/2012   4:49 PM            Program Files (x86)                                        
d----          3/9/2012   9:57 PM            Python27                                                   
d-r--          3/4/2012   8:11 PM            Users                                                      
d----         3/14/2012   3:41 AM            Windows                                                    
-a---          3/4/2012   8:45 PM       1024 .rnd  
Run Code Online (Sandbox Code Playgroud)


Mic*_*ens 7

@Andy提供了一些特别针对您问题中的要点的重要信息.我的答案更多的是考虑到更广泛影响的补充.它可能只是一个评论,但长度和我所包含的图像阻止我发布这只是一个评论...

我最近在Powershell中检查了管道与直接输入的问题,其具体目标是使这些输入流相对于所有类型的输入以及应用的默认值对称.根据我的估算,有六个等价类输入需要考虑:

  • 没有输入
  • 空值
  • 纯量
  • 名单正常
  • 混合值列表(即一些null或空)

当这些输入中的每一个被发送到函数时,通常会期望的是这个相应的列表:

  • 默认值
  • 空值
  • 纯量
  • 名单正常
  • 混合值列表(即一些null或空)

也就是说,如果没有提供输入,则使用默认值; 否则使用给定的值.这听起来几乎是微不足道的,实际上是一个重言式,但也有一些细微之处.例如,考虑通过管道提供无输入意味着什么?它是null还是空集合?我认为后者,除了其他原因之外,它允许我在上面提到的流之间的对称性.此外,你怎么写你的函数签名函数体内,使与一个或另一个输入流中部分或所有这些输入类的,有时令人吃惊的影响.因此,我进一步认为,这种"微不足道"的考虑还有很多,而不是乍看之下.因此,我在Simple-Talk.com上发表的文章" Down the Rabbit Hole- PowerShell管道,函数和参数研究"中广泛撰写了这篇文章 .本文附带的是一个挂图,其中显示了六个等价输入类的表格以及每个具有不同功能模板的内容.这是挂图的缩略图:

在此输入图像描述