2012-03-26 47 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的結果總是字符串。

相關問題