2010-12-28 12 views
10

Una funzione di PowerShell può determinare se viene eseguita come parte di una pipeline? Ho una funzione che popola un array con istanze di FileInfo che vorrei "rendere" alla pipeline se la funzione viene eseguita in questo modo o produrre un output piuttosto carino se la funzione viene invocata da solo dalla riga di comando.Posso determinare se una funzione di PowerShell è in esecuzione come parte di una pipeline?

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 
     } 
    } 
} 

In sostanza, sto cercando di capire come implementare INVOKED_IN_PIPELINE. Se viene eseguito in una pipeline (ad esempio Do-Something | format-table fullname), fornirei semplicemente l'array, ma se eseguito direttamente (ad esempio Do-Something), sarebbe piuttosto stampare il contenuto dell'array sulla console.

C'è un modo per farlo? Se c'è un modo più "idiomatico" per raggiungere questo tipo di cose, sarei anche interessato a sapere.

+1

Perché era questo downvoted? –

risposta

14

Queste informazioni sono disponibili come parte di $ PSCmdlet.MyInvocation. Ecco un cmdlet che puoi usare per sperimentare questo. Che cosa fa scrivere il contenuto di quella proprietà una volta per l'esecuzione di un comando e poi passa sull'oggetto (in modo da poter manipolare di più gli oggetti con pipeline più grandi). Quello che vedrete è che c'è una proprietà chiamata PipelineLength che è uguale a 1 quando si esegue questo comando da solo e aumenta per ogni elemento nella pipeline. Notare anche PipelinePosition. Ti dice quale posizione questo comando è nella pipeline.

NOTA:$PSCmdlet è disponibile solo quando si scrive una funzione avanzata (ad esempio, hanno l'attributo [CmdletBinding()]

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 $_ 
    } 
} 

Ecco alcuni esempi:.

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

Esiste un'opzione quando il comando fa parte del compito piuttosto che chiamato da solo, come ad esempio '$ x = Fare-Qualcosa ...'. In quel caso la posizione del gasdotto non aiuterà. Soluzione alternativa per questo è 'Do-Something | imposta x' ma è facile dimenticare che ... – majkinetor

6

Il modo "idiomatico" per eseguire questa operazione è di generare un tipo di oggetto specifico e quindi definire i dati di formattazione per quell'oggetto. L'oggetto può essere personalizzato (oggetto basato su C#/VB) o PSObject denominato. Il vantaggio di questo approccio è che è possibile emettere solo questi oggetti e se non è disponibile alcuna ulteriore formattazione dell'output della pipeline (ad esempio l'uso di un comando Format), la formattazione di output predefinita verrà utilizzata. In caso contrario, uno dei comandi Format può sovrascrivere quella formattazione predefinita. Ecco un esempio:

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 

Notate come l'output predefinito è diversa la seconda volta che abbiamo inviato $obj giù per il tubo. Questo perché utilizzava le istruzioni di formattazione personalizzate fornite dal momento che non erano usati comandi di formattazione espliciti.

BTW c'è sempre una pipeline anche per $obj perché è collegata in modo implicito a Out-Default.

Ecco la definizione del formato personalizzato che viene definito in un file .ps1xml che ho chiamato MyCustomFormatData.xml:

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

Per ulteriori esempi di come formattare oggetti personalizzati cerca nella uscita file da questo comando:

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

Va bene, ma collega lo script con dati XML che potrebbero non essere accettabili (soprattutto perché le modifiche di $ pshome potrebbero richiedere privilegi elevati) – majkinetor

+2

Non è necessario modificare $ PSHome per installare un file .ps1xml di formattazione. Il file .ps1xml può essere posizionato proprio accanto allo script. Vedi la guida su Update-FormatData. –

Problemi correlati