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 discrepancia con HashTables es fairly well known, aunque el official documentation solo lo menciona oblicuamente (mediante ejemplo).¿Qué determina si la tubería de Powershell desenrollará una colección?

El problema con las funciones, sin embargo, es nuevo para mí. Estoy sorprendido de que no me haya mordido antes. ¿Hay algún principio rector que podamos seguir los scripters? Sé que al escribir cmdlets en C# hay un overload of WriteObject donde puedes controlar la enumeración explícitamente, pero AFAIK no existe tal construcción en el lenguaje Posh. Como muestra el ejemplo final, el intérprete Posh parece creer que no hay diferencia en el tipo de objetos que se canalizan. Sospecho que puede haber alguna rareza de Object vs PSObject bajo el capó, pero eso es de poca utilidad cuando estás escribiendo Posh puro y esperas que el lenguaje de script "simplemente funcione".

/editar/

Keith es correcto señalar que en mi ejemplo, yo estoy de paso en un solo argumento string [] en lugar de 3 argumentos de cadena. En otras palabras, la razón por la cual Measure-Object dice Count = 1 es porque está viendo una única matriz de matrices cuyo primer elemento es @ ("a", "b", "c"). Lo suficientemente justo. Este conocimiento le permite trabajar en torno al tema de varias maneras:

# 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 

Sin embargo, no explica todo ...

# 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 

De lo que puedo extrapolar hay otra regla de juego: si usted tiene una matriz con exactamente un elemento Y el analizador está en expression mode, entonces el intérprete "desenrollará" dicho elemento. ¿Alguna más sutileza que me falta?

+0

El equivalente de WriteObject es el cmdlet Write-Output (alias a echo) que las personas rara vez utilizan porque los valores se envían implícitamente a la corriente stdout. –

+0

Es cierto, aunque Write-Output no tiene un parámetro -EnumerateCollection como WriteObject (object, bool). –

+0

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

Respuesta

11

$ args se desenrolla. Recuerde que los parámetros de función normalmente se pasan usando el espacio para separarlos. Cuando se pasa en 1,2,3 está de paso en un solo argumento de que es un conjunto de tres números que se asigna a $ args [0]:

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

Count : 3 

Poner los resultados (una matriz) dentro de una agrupación expresión (o subexpresión, por ej.$()) hace que sea elegible de nuevo para desenrollar por lo que el siguiente desenrolla el objeto [], que contiene 1,2,3 devuelto por UnrollMe:

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

que es equivalente a:

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

BTW no lo hace solo aplica a una matriz con un elemento.

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

El uso de una subexpresión array (@()) en algo que ya es una matriz no tiene ningún efecto, no importa cuántas veces se lo aplica. :-) Si desea evitar el desenrollado, utilice el operador de coma porque siempre creará otra matriz externa que se desenrollará. Tenga en cuenta que en este escenario que en realidad no prevenir el desenrollado, que acaba de trabajar en todo el desenrollado mediante la introducción de una matriz externa "contenedor" que consigue desenrolló en lugar de su matriz original por ejemplo:

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

Por último, cuando se ejecuta este :

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

Se puede ver que UnrollMe devuelve dos elementos (a, b, c) como una matriz y d como un escalar. Esos dos elementos se envían por la tubería por separado, que es el recuento resultante es 2.

+0

Buena llamada. Para aclarar: en mi ejemplo, $ args no es solo una matriz sino una matriz de matrices. Su primer y único elemento es @ (1,2,3). –

+0

Hmm, en una mayor reflexión, esto genera más preguntas de las que responde :) Ver edición. –

+0

>> Al poner los resultados (una matriz) dentro de una expresión de agrupamiento (o subexpresiones, por ejemplo, $()), vuelve a ser elegible para el despliegue. //// Excelente resumen, gracias. –

1

Parece que tiene algo que ver con cómo funciona Measure-Object y cómo se pasan objetos a lo largo de la tubería.

Cuando dice

1,2,3 | measure 

obtiene 3 Int32 objetos pasados ​​a la tubería, medir objeto cuenta entonces cada objeto que ve en la tubería.

Cuando "desenrollar" la utilización de su función, se obtiene un único objeto matriz pasada a la tubería que los recuentos de medida de objeto como 1, no hace ningún intento para iterar a través de los objetos de la matriz, como se muestra aquí:

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

Una posible solución alternativa es la de "re-roll" la matriz en la tubería usando foreach:

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