2010-12-28 8 views
10

¿Puede una función de PowerShell determinar si se está ejecutando como parte de una tubería? Tengo una función que puebla una matriz con instancias de FileInfo que me gustaría "ceder" a la tubería si la función se está ejecutando de esta manera o producir alguna salida bonita si la función se está invocando por sí misma desde la línea de comandos.¿Puedo determinar si una función de PowerShell se está ejecutando como parte de una canalización?

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

Básicamente, estoy tratando de encontrar la manera de poner en práctica INVOKED_IN_PIPELINE. Si se ejecuta en una canalización (por ejemplo, Do-Something | format-table fullname), simplemente cedería la matriz, pero si se ejecuta directamente (por ejemplo, Do-Something), imprimiría bastante bien el contenido de la matriz en la consola.

¿Hay alguna manera de hacerlo? Si hay una forma más "idiomática" de lograr este tipo de cosas, también me interesaría saber.

+1

¿Por qué este downvoted? –

Respuesta

14

Esta información está disponible como parte de $ PSCmdlet.MyInvocation. Aquí hay un cmdlet que puede usar para experimentar con esto. Lo que hace es escribir el contenido de esa propiedad una vez para cualquier ejecución de comando y luego pasa el objeto (para que pueda manipular los objetos un poco más con tuberías más grandes). Lo que verá es que hay una propiedad llamada PipelineLength que es igual a 1 cuando ejecuta este comando por sí mismo y aumenta para cada elemento en la canalización. También tenga en cuenta PipelinePosition. Le dice qué posición este comando está en tramitación.

NOTA:$PSCmdlet sólo está disponible cuando se escribe una función avanzada (por ejemplo, tienen el atributo [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 $_ 
    } 
} 

He aquí algunos ejemplos:.

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

¿Hay alguna opción cuando el comando es parte de la asignación en lugar de invocarse por sí mismo, como '$ x = Hacer-Algo ...'. En ese caso, la posición de la tubería no ayudará. La solución para esto es 'Hacer algo' | establecer x' pero es fácil olvidar que ... – majkinetor

6

La forma "idiomática" de hacer esto es generar un tipo de objeto específico y luego definir datos de formato para ese objeto. El objeto puede ser un objeto personalizado (objeto C#/VB-based) o un PSObject nombrado. La ventaja de este enfoque es que puede simplemente generar estos objetos y, si no hay más formatos de salida de canalización (es decir, el uso de un comando Format), entonces se define el formato de salida predeterminado que se utilizará. De lo contrario, uno de los comandos Format puede anular ese formato predeterminado. He aquí un ejemplo:

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 

Note como la salida por defecto es diferente la segunda vez que enviamos $obj por el tubo. Esto se debe a que usó las instrucciones de formato proporcionadas, ya que no se usaron comandos de formato explícitos.

BTW siempre hay una tubería incluso para $obj porque está implícitamente conectada a Out-Default.

Aquí es la definición de formato personalizado que se define en un archivo .ps1xml que nombré 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> 

Para más ejemplos de cómo dar formato a los objetos personalizados mirar a la salida archivos por este comando:

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

Eso está bien, pero acopla la secuencia de comandos con datos XML que pueden no ser aceptables (especialmente porque $ pshome cambios pueden requerir valores elevados) – majkinetor

+2

No necesita modificar $ PSHome para instalar un archivo .ps1xml de formato. El archivo .ps1xml se puede ubicar al lado del script. Ver la ayuda en Update-FormatData. –

Cuestiones relacionadas