vectores programacion matriz matrices matematicas definicion clase arrays powershell object syntax pipeline

arrays - matriz - vectores y matrices en programacion



¿Canalizar objetos de matriz completos en lugar de elementos de matriz uno a la vez? (3)

¿Cómo se envía la salida de un CmdLet al siguiente en una tubería como un objeto de matriz completo en lugar de los elementos individuales de la matriz de uno en uno?

El problema - Descripción genérica
Como puede verse en la ayuda para about_pipelines ( help pipeline ), powershell envía objetos uno en el momento por el pipeline down. Entonces Get-Process -Name notepad | Stop-Process Get-Process -Name notepad | Stop-Process envía un proceso en el momento en que se realiza la tubería.

Digamos que tenemos un CmdLet de terceros (Do-SomeStuff) que no se puede modificar ni cambiar de ninguna manera. Do-SomeStuff realiza diferente si se pasa una matriz de cadenas o si se pasa un solo objeto de cadena.

Do-SomeStuff es solo un ejemplo, podría sustituirse por ForEach-Object , Select-Object , Write-Host (o cualquier otro CmdLet que acepte entradas de canalización)

Do-SomeStuff en este ejemplo procesará los elementos individuales de la matriz uno en el momento.

$theArray = @("A", "B", "C") $theArray | Do-SomeStuff

Si queremos enviar la matriz completa como un objeto a Do-SomeStuff, podríamos intentar algo como esto

@($theArray) | Do-SomeStuff

Pero no produce el resultado esperado ya que PowerShell "ignora" la nueva matriz de un solo elemento.

Entonces, ¿cómo se "fuerza" a que $theArray se transmita por la tubería como un único objeto de matriz en lugar de los elementos de contenido uno a la vez?


El problema - ejemplo práctico
Como se muestra a continuación, la salida de Write-Host es diferente si se pasa una matriz o si pasa los elementos individuales en la matriz uno en el momento.

PS C:/> $theArray = @("A", "B", "C") PS C:/> Write-Host $theArray A B C PS C:/> $theArray | foreach{Write-Host $_} A B C PS C:/> @($theArray) | foreach{Write-Host $_} A B C

¿Cómo hacer para obtener $theArray | foreach{Write-Host $_} $theArray | foreach{Write-Host $_} produzca la misma salida que Write-Host $theArray ?



NOTAS

  1. Procesamiento de tubería en Powershell

Un conjunto normal de cadenas

PS C:/> @("A", "B", "C").GetType().FullName System.Object[]


Un conjunto normal de cadenas canalizadas a Foreach-Object

PS C:/> @("A", "B", "C") | foreach{$_.GetType().FullName} System.String System.String System.String

Cada cadena en la matriz se procesa una a la vez por el objeto de objeto ForEach-Object.


Una matriz de matrices, donde las matrices "internas" son matrices de cadenas.

PS C:/> @(@("A", "B", "C"), @("D", "E", "F"), @("G", "H", "I")) | foreach{$_.GetType().FullName} System.Object[] System.Object[] System.Object[]

Cada matriz de la matriz se procesa una a la vez por el objeto CmdLet ForEach-Object, y el contenido de cada sub-matriz de la entrada se trata como un objeto aunque sea una matriz.


El carácter de coma hace que los datos sean una matriz. Para hacer que la línea de tubería procese su matriz como una matriz, en lugar de operar individualmente en cada elemento de la matriz, es posible que también deba ajustar los datos entre paréntesis.

Esto es útil si necesita evaluar el estado de varios elementos en la matriz.

Usando la siguiente función

function funTest { param ( [parameter(Position=1, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [alias ("Target")] [array]$Targets ) # end param begin {} process { $RandomSeed = $( 1..1000 | Get-Random ) foreach ($Target in $Targets) { Write-Host "$RandomSeed - $Target" } # next target } # end process end {} } # end function

Considere los siguientes ejemplos:

El simple hecho de envolver su matriz entre paréntesis no garantiza que la función procesará la matriz de valores en una llamada de proceso. En este ejemplo, vemos los cambios de números aleatorios para cada elemento de la matriz.

PS C:/> @(1,2,3,4,5) | funTest 153 - 1 87 - 2 96 - 3 96 - 4 986 - 5

Simplemente agregando la coma inicial, tampoco garantiza que la función procesará la matriz de valores en una llamada de proceso. En este ejemplo, vemos los cambios de números aleatorios para cada elemento de la matriz.

PS C:/> , 1,2,3,4,5 | funTest 1000 - 1 84 - 2 813 - 3 156 - 4 928 - 5

Con la coma inicial y la matriz de valores entre paréntesis, podemos ver que el número aleatorio permanece igual porque el comando foreach de la función se está aprovechando.

PS C:/> , @( 1,2,3,4,5) | funTest 883 - 1 883 - 2 883 - 3 883 - 4 883 - 5


Hay una solución de la vieja escuela, si no te importa que tu proceso sea una función.

Ejemplo: desea que una matriz se copie en el portapapeles de una manera que le permita construirla de nuevo en otro sistema sin ninguna conectividad PSRemoting. Así que desea que una matriz que contenga "A", "B" y "C" se transmute a una cadena: @ ("A", "B", "C") ... en lugar de una matriz literal.

Entonces construyes esto (que no es óptimo por otras razones, pero te mantienes en el tema):

# Serialize-List param ( [Parameter(Mandatory, ValueFromPipeline)] [string[]]$list ) $output = "@("; foreach ($element in $list) { $output += "`"$element`"," } $output = $output.Substring(0, $output.Length - 1) $output += ")" $output

y funciona cuando se especifica la matriz como un parámetro directamente:

Serialize-List $ list

@("A B C")

... pero no tanto cuando lo pasas por la tubería:

$ lista | Serialize-List

@("DO")

Pero refactorice su función con los bloques de inicio, proceso y finalización:

# Serialize-List param ( [Parameter(Mandatory, ValueFromPipeline)] [string[]]$list ) begin { $output = "@("; } process { foreach ($element in $list) { $output += "`"$element`"," } } end { $output = $output.Substring(0, $output.Length - 1) $output += ")" $output }

... y obtienes la salida deseada en ambos sentidos.

Serialize-List $ list

@("A B C")

$ lista | Serialize-List

@("A B C")


Respuesta corta: usar operador de matriz unaria ,

,$theArray | foreach{Write-Host $_}

Respuesta larga: hay una cosa que debes entender sobre el operador @() : siempre interpreta su contenido como una declaración, incluso si el contenido es solo una expresión. Considere este código:

$a=''A'',''B'',''C'' $b=@($a;) $c=@($b;)

Añado explícitamente el fin de la marca de declaración ; Aquí, aunque PowerShell permite omitirlo. $a es $a matriz de tres elementos. ¿Qué resultado de $a; ¿declaración? $a es una colección, por lo que la colección se debe enumerar y cada elemento individual se debe pasar por canalización. Entonces el resultado de $a; declaración es tres elementos escritos a la tubería. @($a;) ve que hay tres elementos, pero no la matriz original, y crea una matriz a partir de ellos, por lo que $b es una matriz de tres elementos. De la misma manera $c es una matriz de los mismos tres elementos. Así que cuando escribes @($collection) creas una matriz, que copia elementos de $collection , en lugar de una matriz de un solo elemento.