2010-12-28 7 views
10

Kann eine PowerShell-Funktion feststellen, ob sie als Teil einer Pipeline ausgeführt wird? Ich habe eine Funktion, die ein Array mit Instanzen von FileInfo füllt, die ich der Pipeline "nachgeben" möchte, wenn die Funktion auf diese Weise ausgeführt wird oder eine hübsche Ausgabe erzeugt, wenn die Funktion von der Befehlszeile aus aufgerufen wird.Kann ich feststellen, ob eine PowerShell-Funktion als Teil einer Pipeline ausgeführt wird?

function Do-Something { 
    $file_infos = @() 
    # Populate $file_infos with FileInfo instances... 

    if (INVOKED_IN_PIPELINE) { 
     return $file_infos 
    } 
    else { 
     foreach ($file_info in $file_infos) { 
      write-host -foregroundcolor yellow $file_info.fullname 
     } 
    } 
} 

Grundsätzlich, ich versuche, herauszufinden, wie INVOKED_IN_PIPELINE zu implementieren. Wenn es in einer Pipeline ausgeführt wird (z. B. Do-Something | format-table fullname), würde ich einfach das Array liefern, aber wenn es direkt ausgeführt wird (z. B. Do-Something), würde es den Inhalt des Arrays auf die Konsole drucken.

Gibt es eine Möglichkeit, dies zu tun? Wenn es einen "idiomatischen" Weg gibt, um so etwas zu erreichen, würde mich das auch interessieren.

+1

Warum wurde dieser Downvoted? –

Antwort

14

Diese Information ist als Teil von $ PSCmdlet.MyInvocation verfügbar. Hier ist ein Cmdlet, mit dem Sie experimentieren können. Was es tut, um den Inhalt dieser Eigenschaft einmal für irgendeine Befehlsausführung herauszuschreiben und dann das Objekt weiterzuleiten (so können Sie die Objekte mit größeren Pipelines etwas mehr manipulieren). Was Sie sehen werden, ist, dass es eine Eigenschaft namens PipelineLength gibt, die gleich 1 ist, wenn Sie diesen Befehl selbst ausführen und für jedes Element in der Pipeline erhöhen. Beachten Sie auch PipelinePosition. Es zeigt Ihnen, welche Position dieser Befehl ist in der Pipeline.

HINWEIS:$PSCmdlet ist nur verfügbar, wenn Sie eine erweiterte Funktion schreiben (zum Beispiel hat das [CmdletBinding()] Attribut

function test-PSCmdlet 
{ 
[CmdletBinding()] 
param(
[Parameter(ValueFromPipeline=$true)] 
$test 
) 
Begin{ 
    $once = $false 
    } 
process 
    { 
     if (!$once) 
     { 
      write-host ($PSCmdlet.MyInvocation |out-string) 
      $once = $true 
     } 
     Write-Output $_ 
    } 
} 

Hier sind einige Beispiele:.

PS C:\Users\jsnover.NTDEV> test-PSCmdlet 

MyCommand  : test-PSCmdlet 
BoundParameters : {} 
UnboundArguments : {} 
ScriptLineNumber : 1 
OffsetInLine  : 14 
HistoryId  : 61 
ScriptName  : 
Line    : test-PSCmdlet 
PositionMessage : 
        At line:1 char:14 
        + test-PSCmdlet <<<< 
InvocationName : test-PSCmdlet 
PipelineLength : 1 
PipelinePosition : 1 
ExpectingInput : False 
CommandOrigin : Runspace 


PS C:\Users\jsnover.NTDEV> gps lsass | test-PSCmdlet |Format-table Name,Id -auto 

MyCommand  : test-PSCmdlet 
BoundParameters : {[test, System.Diagnostics.Process (lsass)]} 
UnboundArguments : {} 
ScriptLineNumber : 1 
OffsetInLine  : 26 
HistoryId  : 62 
ScriptName  : 
Line    : gps lsass | test-PSCmdlet |Format-table Name,Id -auto 
PositionMessage : 
        At line:1 char:26 
        + gps lsass | test-PSCmdlet <<<< |Format-table Name,Id -aut 
        o 
InvocationName : test-PSCmdlet 
PipelineLength : 3 
PipelinePosition : 2 
ExpectingInput : True 
CommandOrigin : Runspace 


Name Id 
---- -- 
lsass 620 
+1

Gibt es eine Option, wenn der Befehl Teil der Zuweisung ist und nicht selbst aufgerufen wird, z. B. '$ x = Do-Something ...'. In diesem Fall hilft die Pipeline-Position nicht. Workaround dafür ist 'Do-Something | Setzen Sie x', aber es ist leicht zu vergessen, dass ... – majkinetor

6

Die "idiomatische" Möglichkeit besteht darin, einen bestimmten Objekttyp auszugeben und dann Formatierungsdaten für dieses Objekt zu definieren. Das Objekt kann ein benutzerdefiniertes (C#/VB-basiertes Objekt) oder ein benanntes PSObject sein. Der Vorteil dieses Ansatzes besteht darin, dass Sie diese Objekte nur ausgeben können, und wenn keine weitere Formatierung der Pipeline-Ausgabe erfolgt (z. B. Verwendung eines Befehls Format), dann wird die Standardausgabeformatierung verwendet. Andernfalls kann einer der Befehle Format diese Standardformatierung überschreiben. Hier ein Beispiel:

PS> $obj = new-object psobject -Property @{FName = 'John'; LName = 'Doe'; ` 
              BirthDate = [DateTime]"5/7/1965"} 
PS> $obj.psobject.TypeNames.Insert(0, "MyNamespace.MyCustomTypeName") 
PS> $obj 

BirthDate        FName       LName 
---------        -----       ----- 
5/7/1965 12:00:00 AM     John        Doe 


PS> Update-FormatData .\MyCustomFormatData.ps1xml 
PS> $obj 

FName      LName      BirthDate 
-----      -----      --------- 
John      Doe      5/7/1965 12:00:00 AM 

Beachten Sie, wie die Standard-Ausgabe unterscheidet das zweite Mal, dass wir $obj hinunter das Rohr geschickt. Dies liegt daran, dass die benutzerdefinierten Formatierungsanweisungen verwendet wurden, da keine expliziten Formatierungsbefehle verwendet wurden.

Übrigens gibt es immer eine Pipeline sogar für $obj, weil das implizit an Out-Default weitergeleitet wird.

Hier ist die benutzerdefinierte Formatdefinition, die in einer .ps1xml Datei, die ich MyCustomFormatData.xml genannt definiert ist:

<Configuration> 
    <ViewDefinitions> 
    <View> 
     <Name>MyNamespace.MyCustomTypeName</Name> 
     <ViewSelectedBy> 
     <TypeName>MyNamespace.MyCustomTypeName</TypeName> 
     </ViewSelectedBy> 
     <TableControl> 
     <TableHeaders> 
      <TableColumnHeader> 
      <Label>FName</Label> 
      <Width>25</Width> 
      <Alignment>left</Alignment> 
      </TableColumnHeader> 
      <TableColumnHeader> 
      <Label>LName</Label> 
      <Width>25</Width> 
      <Alignment>left</Alignment> 
      </TableColumnHeader> 
      <TableColumnHeader> 
      <Label>BirthDate</Label> 
      <Width>25</Width> 
      <Alignment>left</Alignment> 
      </TableColumnHeader> 
     </TableHeaders> 
     <TableRowEntries> 
      <TableRowEntry> 
      <TableColumnItems> 
       <TableColumnItem> 
       <PropertyName>FName</PropertyName> 
       </TableColumnItem> 
       <TableColumnItem> 
       <PropertyName>LName</PropertyName> 
       </TableColumnItem> 
       <TableColumnItem> 
       <PropertyName>BirthDate</PropertyName> 
       </TableColumnItem> 
      </TableColumnItems> 
      </TableRowEntry> 
     </TableRowEntries> 
     </TableControl> 
    </View> 
    </ViewDefinitions> 
</Configuration> 

Objekte Weitere Beispiele dafür, wie die Formatierung Brauch bei den Dateien ausgegeben durch diesen Befehl aus:

Get-ChildItem $pshome\*.format.ps1xml 
+0

Das ist in Ordnung, aber es koppelt das Skript mit XML-Daten, die möglicherweise nicht akzeptabel sind (besonders weil $ pshome Änderungen erhöhte privs erfordern) – majkinetor

+2

Sie müssen $ PSHome nicht ändern, um eine Formatierung .ps1xml-Datei zu installieren. Die .ps1ml-Datei kann sich direkt neben dem Skript befinden. Siehe Hilfe zu Update-FormatData. –

Verwandte Themen