2009-12-01 11 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 

Die Diskrepanz mit HashTables ist fairly well known, obwohl die official documentation es nur schräg erwähnt (über Beispiel).Was bestimmt, ob die Powershell-Pipeline eine Sammlung ausrollen soll?

Das Problem mit Funktionen ist jedoch neu für mich. Ich bin irgendwie geschockt, dass es mich jetzt nicht gebissen hat. Gibt es ein Leitprinzip, dem wir Skripter folgen können? Ich weiß, dass beim Schreiben von Cmdlets in C# overload of WriteObject, wo Sie die Aufzählung explizit steuern können, aber AFAIK gibt es kein solches Konstrukt in der Posh Sprache selbst. Wie das letzte Beispiel zeigt, scheint der Posh-Interpreter zu glauben, dass es keinen Unterschied in der Art der zu verrohrenden Objekte gibt. Ich vermute, dass es unter der Haube einige Object vs PSObject-Seltsamkeit gibt, aber das ist nutzlos, wenn Sie reines Posh schreiben und erwarten, dass die Skriptsprache "einfach funktioniert".

/EDIT/

Keith ist richtig darauf hinweisen, dass in meinem Beispiel, ich bin in einem einzigen String [] Argumente übergeben, anstatt 3 String-Argumente. Mit anderen Worten, der Grund, warum Measure-Object Count = 1 sagt, ist, weil es ein einzelnes Array-of-arrays sieht, dessen erstes Element @ ist ("a", "b", "c"). Meinetwegen. Dieses Wissen ermöglicht es Ihnen, in mehr Möglichkeiten, um das Problem zu umgehen:

# 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 

Aber es erklärt nicht alles ...

# 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 

Von dem, was ich extrapolieren kann eine andere Regel im Spiel gibt es: wenn Sie haben ein Array mit genau einem Element UND der Parser ist in expression mode, dann wird der Interpreter das Element "auspacken". Noch mehr Feinheiten, die ich vermisse?

+0

Das Äquivalent von WriteObject ist das Cmdlet Write-Output (aliased to echo), das nur selten verwendet wird, da Werte implizit an den Stdout-Stream ausgegeben werden. –

+0

True, obwohl Write-Output keinen -EnumerateCollection-Parameter wie WriteObject (object, bool) hat. –

+0

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

Antwort

11

ist $ args abgerollt. Denken Sie daran, dass Funktionsparameter normalerweise mit Leerzeichen übergeben werden, um sie zu trennen. Wenn Sie in 1,2,3 passieren vorbei Sie werden in einem einzigen Argument, das eine Reihe von drei Zahlen, die bis $ args zugewiesen wird [0]:

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

Count : 3 

die Ergebnisse Putting (ein Array) innerhalb einer Gruppierung Ausdruck (oder Unterausdruck zdas Objekt [], enthaltend 1,2,3 zurückgegeben durch UnrollMe $()) macht es wieder, die für Abrollen so folgendes entrollt:

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

, die äquivalent ist:

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

BTW nicht wahr Gilt nur für ein Array mit einem Element.

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

ein Array subexpression Verwendung (@()) auf etwas, das bereits ein Array hat keine Auswirkung, egal wie oft Sie es anwenden. :-) Wenn Sie das Abrollen verhindern möchten, verwenden Sie den Komma-Operator, weil immer ein weiteres äußeres Array erstellt wird, das abgerollt wird. Beachten Sie, dass Sie in diesem Szenario nicht wirklich Abrollen verhindern, arbeiten Sie gerade um die Abrollen durch eine äußere „Wrapper“ Array einzuführen, die anstelle des ursprünglichen Arrays zB abgerollt wird:

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

Schließlich, wenn Sie dies ausführen :

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

Man kann sehen, dass UnrollMe zwei Elemente zurückgibt (a, b, c) als Array und d als ein Skalar. Diese beiden Elemente werden separat in die Pipeline gesendet. Die resultierende Anzahl ist 2.

+0

Guter Anruf. Zur Klarstellung: In meinem Beispiel ist $ args nicht nur ein Array, sondern ein Array-of-arrays. Das erste und einzige Element ist @ (1,2,3). –

+0

Hmm, bei weiterer Betrachtung wirft das mehr Fragen auf als es beantwortet :) Siehe Bearbeiten. –

+0

>> Wenn Sie die Ergebnisse (ein Array) innerhalb eines Gruppierungsausdrucks (oder Teilausdrucks, z. B. $()) einfügen, ist es wieder zum Abrollen berechtigt. //// Ausgezeichnete Zusammenfassung, danke. –

1

Es scheint etwas damit zu tun, wie Measure-Object funktioniert und wie Objekte entlang der Pipeline übergeben werden.

Wenn Sie

1,2,3 | measure 

sagen Sie erhalten 3 Int32 Gegenstände auf die Pipeline übergeben, messen Objekt zählt dann jedes Objekt auf der Pipeline sieht.

Wenn Sie „es entrollen“ Ihre Funktion verwenden, erhalten Sie ein einzelnes Array-Objekt auf die Pipeline geleitet, die Maßnahme Objekt zählt als 1, es nicht versucht, durch die Objekte in dem Array zu durchlaufen, wie hier gezeigt:

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

Eine mögliche Behelfslösung ist zu "re-roll" die Anordnung auf die Rohrleitung unter Verwendung von foreach:

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