2016-02-26 33 views

risposta

9

Questo ha a che fare con il modo le opere di pipeline. Se si utilizza un cmdlet diverso, come Write-Verbose che funzionerà come previsto:

$t = "before" 
1..2 | Tee-Object -Variable t | Write-Verbose -Verbose 

Si consideri che nel corso di un oleodotto, ogni funzione o cmdlet possono utilizzare un Begin, Process e End blocco.

Tutti i blocchi Begin vengono eseguiti per primi, quindi Process viene chiamato una volta per ogni oggetto pipeline e quindi tutti i numeri End s.

L'assegnazione variabile deve necessariamente avvenire nel blocco End.

A partire da PowerShell v3, i cmdlet possono interrompere la pipeline.

Questo è ottimo per qualcosa come Select-Object -First 1 perché significa che l'intera pipeline può essere fermata dopo il primo oggetto, invece di eseguire una volta per ciascun elemento anche se il resto verrà scartato.

Ma significa anche che il blocco End non viene mai eseguito.

se si avvia PowerShell in v2: powershell.exe -Version 2.0

e quindi eseguire il secondo esempio, che funzionerà come previsto perché la pipeline non poteva essere prematuramente interrotto in quella versione.


Ecco una dimostrazione:

function F1 { 
[CmdletBinding()] 
param(
    [Parameter(ValueFromPipeline)] 
    $o 
) 

    Begin { 
     Write-Verbose "Begin" -Verbose 
    } 

    Process { 
     Write-Verbose $o -Verbose 
     $o 
    } 

    End { 
     Write-Verbose "End" -Verbose 
    } 
} 

Poi chiamarlo:

1..2 | F1 

vs.

1..2 | F1 | Select-Object -First 1 

Si potrebbe anche dimostrare questo con ForEach-Object:

012.
1..2 | ForEach-Object -Begin { 
    Write-Verbose "Begin" -Verbose 
} -Process { 
    Write-Verbose $_ -Verbose 
    $_ 
} -End { 
    Write-Verbose "End" -Verbose 
} 

vs.

1..2 | ForEach-Object -Begin { 
    Write-Verbose "Begin" -Verbose 
} -Process { 
    Write-Verbose $_ -Verbose 
    $_ 
} -End { 
    Write-Verbose "End" -Verbose 
} | Select-Object -First 1 

Secondo the documentation you linked, è possibile utilizzare il parametro -Wait per disattivare questa ottimizzazione:

$t = "before" 
1..2 | Tee-Object -Variable t | Select-Object -First 1 -Wait 

Questo popolerà $t ma forse non con il valore che si voleva. Conterrà @(1,2), presumibilmente poiché lo -OutVariable è stato inserito su Tee-Object e non su Select-Object.

Ricordare che il "risultato" della pipeline è ciò che viene restituito dall'esecuzione (lato sinistro dello =) e che è corretto in tutte le istanze.

-OutVariable è qualcosa implementato da alcuni cmdlet e molto probabilmente deve essere implementato nel blocco End per quel particolare cmdlet, quindi per prevedere cosa darà sarà altamente dipendente dalla comprensione del flusso di esecuzione della pipeline.

Quindi, per rispondere alla domanda nel tuo commento, mi sembra che sia stata implementata correttamente. Sto fraintendendo la tua affermazione?

+0

Riguardo alle specifiche di "Select-Object -First" (https://technet.microsoft.com/en-us/library/hh849895.aspx) e "Tee-Object -Variable" (https://technet.microsoft .com/it/us/library/hh849937.aspx) L'ottimizzazione della pipeline di Powershell 3+ non è stata implementata correttamente. L'implementazione corretta dovrebbe dare risultato @ (1) - flusso rotto dopo il primo elemento o @ (1,2) - flusso non trattato. Sei d'accordo? – jjacek

+0

@jjacek vedi edit – briantist

+1

L'ordine di valutazione forzato (dividendo efficacemente la pipeline in due) con parentesi è un'altra opzione: '(1..2 | tee -variable t) | select -first 1' –