2012-03-26 49 views
11

我想写一个函数,它需要多个参数,它可以来自命令行,也可以来自管道。参数可以是字符串或目录对象。这个想法是下列任何调用的应工作:如何创建一个接受来自管道和命令行的多个参数类型的函数?

Test-VEnv '.\MyPath', '.\AnotherPath' 
Test-VEnv (dir) 
'MyPath', 'AnotherPath' | Test-VEnv 
dir | Test-VEnv 

下面的代码几乎作品:

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

    process { 
     foreach ($P in $Path) { 
      ... 
     } 
    } 
} 

它可以处理从管道和命令参数字符串,并处理目录对象从管道(通过ValueFromPipelineByPropertyName和FullName别名)。不过,这并不在命令行处理目录对象,所以

dir | Where-Object { Test-VEnv $_ } 

失败,因为它转换目录对象为字符串,它使用的名称属性,而不是全名,和随后的代码失败。

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

我知道,即使我能得到这个工作,它可能不是一个特别好的设计。但据我所知,这就是内置测试路径的工作原理,所以我想在创建自己的之前尝试遵循标准行为...

回答

9

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

您可以使用Trace-Command来查看PowerShell如何绑定输入对象。下面是如何使用它的一个例子:

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

这里是输出的重要组成部分:

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 

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 
  1. 由于我的PWD是不是C:\我得到错误的,因为DirectoryInfo对象转换为字符串(ToString())只对文件夹的名称。这是因为管道没有被使用。

  2. 由于管道使用它的作品,因为它是与此参数结合PsPath:

    [Parameter(ParameterSetName='LiteralPath', Mandatory=$true, ValueFromPipelineByPropertyName=$true)] 
    [Alias('PSPath')] 
    [string[]] 
    ${LiteralPath}, 
    
  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 
    } 
} 

一些测试:

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 

输出:

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 
+0

好的,所以你说的是我的函数的行为就像测试路径一样。我的道歉,我似乎让我的测试有点混乱 - 谢谢澄清我。那么这是否意味着不可能实现我想要的结果? (至少在代码中没有不明确的类型检查) – 2012-03-27 08:45:38

+0

@PaulMoore无论是否使用了管道,你是否期望完整路径作为字符串绑定到'$ Path'? – 2012-03-27 09:11:11

+0

@PaulMoore尝试使用'PsPath'这是'Test-Path'使用的。我用一些例子更新了我的答案。 – 2012-03-27 19:17:30

0

请问如果从字符串更改$ PATH的类型[]到它的工作[系统.IO.DirectoryInfo []]?

+0

目前还不清楚OP是否想要处理FileInfos和DirectoryInfos。由于这个例子使用了路径,所以我的猜测都被使用了。 – 2012-03-27 09:34:01

+0

你可能是对的。我添加了这个基础:'参数可以是字符串或目录对象'。 – 2012-03-27 09:55:26

+0

如果'Test-VEnv'只能用于目录,这就是我所要做的。 – 2012-03-27 10:01:45

7

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

我最近审查了管道与直接输入在Powershell的问题,具体目标是使这些输入流相对于所有类别的输入和对于应用什么默认值是对称的。还有,据我估计,输入六个等价类来考虑:

  • 没有输入
  • 正常
  • 名单名单混合值(即一些空或空)

什么时,每个这些输入发送到功能将这种相应的列表中选择一个通常会想到:

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

也就是说,在没有提供输入的情况下使用默认值;否则使用给定的值。这听起来几乎微不足道,实际上是同义反复,但也有一些微妙之处。例如,考虑通过管道供应没有输入是什么意思?它是空的还是空的集合?除了其他原因之外,我认为后者允许上面提到的流之间的对称性。此外,您如何编写这两个您的函数签名您的函数体有时会使用一个或另一个输入流对某些或所有这些输入类产生令人惊讶的影响。因此,我进一步认为,这种“微不足道”的考虑比乍看之下还要多得多。以至于我在Simple-Talk.com上发布的文章 Down the Rabbit Hole- A Study in PowerShell Pipelines, Functions, and Parameters, 中写了大量的内容。文章中包含一个挂图,其中显示了六个等价输入类的表格,以及您使用不同功能模板获得的每个类别的表格。这里是挂图的缩略图:

enter image description here

0
function Install-PathTransformation 
{ 
    [CmdletBinding()] 
    param() 

    if (-not $script:my_pathtransformation_types) { 
     $script:my_pathtransformation_types = Add-Type -TypeDefinition @" 
     using System; 
     using System.IO; 
     using System.Management.Automation; 

     public class ValidPathTransformationAttribute : ArgumentTransformationAttribute { 
      public bool Resolve { 
       get; 
       set; 
      } 

      public override Object Transform(EngineIntrinsics engineIntrinsics, Object inputObject) { 
       PSObject psobj = inputObject as PSObject; 
       if (psobj != null) 
        inputObject = psobj.BaseObject; 
       if (inputObject == null) 
        return inputObject; 

       FileSystemInfo test1 = inputObject as FileSystemInfo; 
       if (test1 != null) 
        return test1.FullName; // no need for further checks, path shoul de qualified! 

       PathInfo test2 = inputObject as PathInfo; 
       if (test2 != null) 
        return test2.Path;  // no need for further checks, path shoul de qualified! 

       string test3 = inputObject as string; 
       if (test3 == null) 
        test3 = (string)LanguagePrimitives.ConvertTo(inputObject, typeof(string)); 
       if (Resolve) 
        test3 = engineIntrinsics.SessionState.Path.GetUnresolvedProviderPathFromPSPath(test3); 
       else if (!engineIntrinsics.SessionState.Path.IsValid(test3)) 
        throw new ArgumentTransformationMetadataException("Invalid path value: " + test3); 
       return test3; 
      } 
     } 
"@ 
    } 
    return $script:my_pathtransformation_types 
} 


Install-PathTransformation 

function A(
    [parameter(Mandatory=$false, ValueFromPipeline=$true)] 
    [ValidPathTransformation(Resolve=$true)] 
    [string] # optional, transformation returns always string 
    $z) { 
    Process { 
    Write-Host $("{0}: {1}" -f $z.GetType().FullName, $z) 
    } 
} 

& { 
    'mumu', 10, 10.5, "" 
    dir $env:Temp | select -First 5 
} | A 

工作原理:
1)创建一个转换属性来处理参数值。
2)在转换过程中,如果Value是FileSystemInfo或PathInfo,我们将其中的值取出,如果不是,我们将值转换为字符串,并确保“路径”有效(并根据需要解析路径)。
3)应用时,Transformation的结果总是字符串。

相关问题