2009-12-01 8 views
9
# array 
C:\> (1,2,3).count 
3 
C:\> (1,2,3 | measure).count 
3 

# hashtable 
C:\> @{1=1; 2=2; 3=3}.count 
3 
C:\> (@{1=1; 2=2; 3=3} | measure).count 
1 

# array returned from function 
C:\> function UnrollMe { $args } 
C:\> (UnrollMe a,b,c).count 
3 
C:\> (UnrollMe a,b,c | measure).count 
1 
C:\> (1,2,3).gettype() -eq (UnrollMe a,b,c).gettype() 
True 

La discrepanza con HashTables è fairly well known, sebbene lo official documentation lo menzioni solo in modo obliquo (tramite l'esempio).Che cosa determina se la pipeline di PowerShell dispiegherà una raccolta?

Il problema con le funzioni, tuttavia, è una novità per me. Sono un po 'scioccato che non mi abbia morso prima d'ora. C'è qualche principio guida che gli sceneggiatori possono seguire? So che scrivendo cmdlet in C# c'è un overload of WriteObject in cui è possibile controllare esplicitamente l'enumerazione, ma AFAIK non esiste un tale costrutto nel linguaggio Posh stesso. Come mostra l'esempio finale, l'interprete di Posh sembra credere che non ci siano differenze nel tipo di oggetti che vengono condotti. Ho il sospetto che ci possa essere qualche stranezza Object vs PSObject sotto il cofano, ma questo è di scarso utilizzo quando si scrive puro Posh e si aspetta che il linguaggio dello script "funzioni solo".

/EDIT/

Keith è corretto sottolineare che nel mio esempio, sto passando in una singola stringa argomento [], piuttosto che 3 argomenti stringa. In altre parole, la ragione per cui Measure-Object dice Count = 1 è perché sta vedendo un singolo array di array il cui primo elemento è @ ("a", "b", "c"). Giusto. Questa conoscenza permette di lavorare intorno al problema in vari modi:

# stick to single objects 
C:\> (UnrollMe a b c | measure).count 
3 

# rewrite the function to handle nesting 
C:\> function UnrollMe2 { $args[0] } 
C:\> (UnrollMe2 a,b,c | measure).count 
3 

# ditto 
C:\> function UnrollMe3 { $args | %{ $_ } } 
C:\> (UnrollMe3 a,b,c | measure).count 
3 

Tuttavia, non spiega tutto ...

# as seen earlier - if we're truly returning @(@("a","b","c")) why not count=1? 
C:\> (UnrollMe a,b,c).count 
3 

# our theory must also explain these results: 
C:\> ((UnrollMe a,b,c) | measure).count 
3 
C:\> (@(@("a","b","c")) | measure).count 
3 
C:\> ((UnrollMe a,b,c d) | measure).count 
2 

Da quello che posso estrapolare c'è un'altra regola in gioco: se si dispone di un array con esattamente un elemento E il parser è in expression mode, quindi l'interprete "scribaccherà" detto elemento. Altre sottigliezze che mi mancano?

+0

L'equivalente di WriteObject è il cmdlet Write-Output (con alias di echo) che gli utenti utilizzano raramente poiché i valori vengono emessi in modo implicito nel flusso stdout. –

+0

È vero, sebbene Write-Output non abbia un parametro -EnumerateCollection come WriteObject (oggetto, bool). –

+0

correlati: http://stackoverflow.com/questions/28702588/in-what-conditions-does-powershell-unroll-items-in-the-pipeline/28707054#28707054 – alx9r

risposta

11

$ args viene srotolato. Ricorda che i parametri della funzione vengono normalmente passati usando lo spazio per separarli. Quando si passa a 1,2,3 si passa in un unico argomento che è un array di tre numeri che viene assegnato a $ args [0]:

PS> function UnrollMe { $args } 
PS> UnrollMe 1 2 3 | measure 

Count : 3 

Mettendo i risultati (un array) all'interno di un raggruppamento espressione (o sottoespressione es$()) rende idoneo nuovo per srotolare modo seguente srotola dell'oggetto [] contenenti 1,2,3 restituito dal UnrollMe:

PS> ((UnrollMe 1,2,3) | measure).Count 
3 

che è equivalente a:

PS> ((1,2,3) | measure).Count 
3 

proposito non è così basta applicare a un array con un elemento.

PS> ((1,2),3) | %{$_.GetType().Name} 
Object[] 
Int32 

Utilizzando un sottoespressione array (@()) su qualcosa che è già una matrice non ha alcun effetto, non importa quante volte lo si applica. :-) Se vuoi impedire lo srotolamento usa l'operatore virgola perché sarà sempre creare un altro array esterno che viene srotolato. Si noti che in questo scenario non si ha realmente impedire srotolando, basta aggirare lo srotolamento con l'introduzione di una serie esterna "wrapper" che viene srotolata invece della matrice originale, ad es:

PS> (,(1,2,3) | measure).Count 
1 

Infine, quando si esegue questo :

PS> (UnrollMe a,b,c d) | %{$_.GetType().Name} 
Object[] 
String 

Si può vedere che UnrollMe restituisce due voci (a, b, c) come un array e d come uno scalare. Questi due elementi vengono inviati separatamente lungo la pipeline, il conteggio risultante è 2.

+0

Buona chiamata. Per chiarire: nel mio esempio, $ args non è solo un array ma un array di array. Il suo primo e unico elemento è @ (1,2,3). –

+0

Hmm, su ulteriori riflessioni questo solleva più domande che risposte :) Vedi modifica. –

+0

>> L'inserimento dei risultati (una matrice) all'interno di un'espressione di raggruppamento (o sottoespressione, ad esempio $()) lo rende nuovamente idoneo per lo srotolamento. //// Eccellente riassunto, grazie. –

1

Sembra avere qualcosa a che fare con il funzionamento di Measure-Object e il modo in cui gli oggetti vengono passati lungo la pipeline.

Quando si dice

1,2,3 | measure 

si ottiene 3 Int32 oggetti passati sul gasdotto, misurare oggetto quindi conta ogni oggetto che vede sul gasdotto.

Quando si "srotolano" di utilizzare la funzione, si ottiene un singolo oggetto array passati al gasdotto che conta misura oggetto come 1, non fa alcun tentativo di scorrere gli oggetti nella matrice, come illustrato di seguito:

PS C:\> (measure -input 1,2,3).count 
1 

Una possibile work-around è quello di "ri-roll" l'array sul gasdotto utilizzando foreach:

PS C:\> (UnrollMe 1,2,3 | %{$_} | measure).count 
3 
Problemi correlati