PowerShell: la salida de escritura solo escribe un objeto

Resuelto Scopperloit asked hace 8 años • 3 respuestas

Estoy aprendiendo PowerShell y una gran cantidad de artículos que leo desaconsejan enfáticamente el uso de write-host, diciéndome que es una "mala práctica" y, casi siempre, el resultado se puede mostrar de otra manera.

Entonces, sigo el consejo y trato de evitar el uso de write-host. Una sugerencia que encontré fue utilizar la salida de escritura en su lugar. Hasta donde tengo entendido, esto pone todo en una canalización y el resultado se ejecuta al final del script (?).

Sin embargo, tengo problemas para generar lo que quiero. Este ejemplo demuestra el problema:

$properties = @{'OSBuild'="910ef01.2.8779";
                'OSVersion'="CustomOS 3";
                'BIOSSerial'="A5FT-XT2H-5A4B-X9NM"}
$object = New-Object –TypeName PSObject –Prop $properties
Write-output $object

$properties = @{'Site'="SQL site";
                'Server'="SQL Server";
                'Database'="SQL Database"}
$object = New-Object –TypeName PSObject –Prop $properties
Write-Output $object

De esta manera obtengo un buen resultado del primer objeto que muestra los datos del sistema operativo, pero el segundo objeto que contiene los datos SQL nunca se muestra. Intenté cambiar el nombre de las variables y muchas otras cosas diferentes, pero no tuve suerte.

Mientras solucionaba este problema, encontré problemas similares con sugerencias para simplemente reemplazar la salida de escritura con el host de escritura. Esto me confunde mucho. ¿Por qué algunas personas desaconsejan fuertemente el write-host, mientras que otras lo alientan?

¿Y cómo exactamente puedo generar estos dos objetos de manera elegante? No entiendo completamente el mecanismo de canalización de salida de escritura.

Scopperloit avatar Oct 26 '16 15:10 Scopperloit
Aceptado
  • Sólo para aclarar: el problema es sólo un problema de visualización :

    • Al enviar a la consola , si el primer objeto tiene formato de tabla (si Format-Tablese aplica, lo que sucede implícitamente en su caso), las columnas de visualización se bloquean en función de las propiedades de ese primer objeto .
      Dado que su segundo objeto de salida no comparte propiedades con el primero, no contribuye en nada a la visualización de la tabla y, por lo tanto, es efectivamente invisible.
    • Por el contrario, si procesa mediante programación la salida del script (asígnela a una variable o envíe su salida a través de la canalización a otro comando), ambos objetos estarán allí.
      • Consulte la respuesta de Charlie Joynt para ver un ejemplo útil de cómo asignar los dos objetos de salida a variables separadas.
  • La solución más sencilla al problema de visualización es formatear explícitamente para mostrar cada objeto de entrada individualmente ; consulte a continuación.


Para un único objeto determinado dentro de un script, puede forzar la salida formateada para mostrar (para alojar) conOut-Host :

$object | Out-Host # same as: Write-Output $object | Out-Host

Sin embargo, tenga en cuenta que esto genera directa e invariablemente solo a la consola y el objeto no forma parte de la salida de datos del script (los objetos escritos en el flujo de salida exitoso , el flujo con índice 1).
En otras palabras: si intenta asignar la salida del script a una variable o enviar su salida a otro comando en una canalización, ese objeto no estará allí.

Vea a continuación por qué Out-Hostes preferible Write-Hosty por qué es mejor evitarlo Write-Hosten la mayoría de las situaciones.

Para aplicar la técnica ad hoc a la salida de un script determinado en su conjunto, para asegurarse de ver todos los objetos de salida , use:

./some-script.ps1 | % { $_ | Out-String }  # % is the built-in alias of ForEach-Object

Tenga en cuenta que aquí también puede usar Out-Host, pero la ventaja de usar Out-Stringes que aún le permite capturar la representación para visualización en un archivo, si lo desea.

Aquí hay una función auxiliar simple (filtro) que puede colocar en su $PROFILE:

# Once defined, you can use: ./some-script.ps1 | Format-Each
Filter Format-Each { $_ | Out-String }

La sugerencia de PetSerAl./some-script.ps1 | Format-List también funciona en principio, pero cambia la salida de la salida de estilo de tabla habitual a la salida de estilo de lista , con cada propiedad enumerada en su propia línea, lo que puede no ser deseado.
Sin embargo, a la inversa Format-Each, si un objeto de salida tiene (implícitamente) formato de tabla, imprime un encabezado para cada objeto.


Por qué Write-Outputno ayuda:

Write-Outputno ayuda, porque de todos modos escribe donde van los objetos de salida de forma predeterminada: el flujo de salida exitoso antes mencionado, donde deben ir los datos .

Si los objetos del flujo de salida no se redirigen o capturan de alguna forma, se envían al host de forma predeterminada (normalmente, la consola ), donde se aplica el formato automático.

Además, el uso de Write-Outputrara vez es necesario , porque simplemente no capturar o redirigir un comando o expresión escribe implícitamente en el flujo de éxito; Otra forma de decirlo:
Write-Outputestá implícito .

Por tanto, las siguientes dos afirmaciones son equivalentes:

Write-Output $object  # write $object to the success output stream
$object               # same; *implicitly* writes $object to the success output stream

Write-HostPor qué no es aconsejable el uso de , tanto aquí como a menudo en general:

Suponiendo que conoce las implicaciones del uso Write-Hosten general (ver más abajo), podría usarlo para el problema en cuestión, pero Write-Hostaplica un formato simple .ToString()a su entrada , lo que no le brinda el formato agradable de varias líneas que aplica PowerShell de forma predeterminada.
Por lo tanto, Out-Host(y Out-String) se usaron arriba, porque aplican el mismo formato amigable.

[hashtable]Contraste las dos declaraciones siguientes, que imprimen un literal de tabla hash ( ):

# (Optional) use of Write-Output: The friendly, multi-line default formatting is used.
# ... | Out-Host and ... | Out-String would print the same.
PS> Write-Output @{ foo = 1; bar = 'baz' }

Name                           Value
----                           -----
bar                            baz
foo                            1

# Write-Host: The hashtable's *entries* are *individually* stringified
#             and the result prints straight to the console.
PS> Write-Host @{ foo = 1; bar = 'baz' }

System.Collections.DictionaryEntry System.Collections.DictionaryEntry    

Write-HostHice dos cosas aquí, lo que resultó en un resultado casi inútil:

  • Se enumeraron las [hashtable]entradas de la instancia y cada entrada se encadenó individualmente.

  • La .ToString()cadena de entradas de la tabla hash (pares clave-valor) es System.Collections.DictionaryEntry, es decir, simplemente el nombre de tipo de la instancia.

Las razones principales para evitar Write-Host en general son :

  • Sale directamente al host (consola) en lugar de al flujo de salida exitoso de PowerShell.

    • Como principiante, puedes pensar erróneamente que Write-Hostes para escribir resultados (datos), pero no es así.
  • Al omitir el sistema de flujos de PowerShell, Write-Hostla salida no se puede redirigir , es decir, no se puede suprimir ni capturar (en un archivo o variable).

    • Dicho esto, a partir de PowerShell v5.0, ahora puede redirigir su salida a través del flujo de información recién introducido (número 6; por ejemplo, ./some-script.ps1 6>write-host-output.txt); sin embargo, esa secuencia se utiliza mejor con el nuevo Write-Informationcmdlet.
      Por el contrario, Out-Hostla salida todavía no se puede redirigir.

Eso deja solo los siguientes usos legítimos deWrite-Host :

  • Creación de indicaciones para el usuario final y representaciones coloreadas solo para visualización :

    • Su secuencia de comandos puede tener mensajes interactivos que soliciten información del usuario; usar Write-Host(opcionalmente con colores a través de los parámetros -ForegroundColory -BackgroundColor) es apropiado, dado que las cadenas de mensajes no deben formar parte de la salida del script y los usuarios también proporcionan sus entradas a través del host (normalmente a través de Read-Host).

    • De manera similar, puede utilizar Write-Hostcoloración selectiva para crear explícitamente representaciones más amigables solo para visualización .

  • Creación rápida de prototipos : si desea una forma rápida y sencilla de escribir información de estado/diagnóstico directamente en la consola sin interferir con la salida de datos de un script .

    • Sin embargo, generalmente es mejor utilizarlo Write-Verboseen Write-Debugtales casos.
mklement0 avatar May 18 '2018 17:05 mklement0

En términos generales, la expectativa es que los scripts/funciones devuelvan un único "tipo" de objeto, a menudo con muchas instancias. Por ejemplo, Get-Processdevuelve una gran cantidad de procesos, pero todos tienen los mismos campos. Como habrá visto en los tutoriales, etc., luego puede pasar la salida a Get-Processlo largo de una canalización y procesar los datos con cmdlets posteriores.

En su caso, está devolviendo dos tipos diferentes de objetos (es decir, con dos conjuntos diferentes de propiedades). PS genera el primer objeto, pero no el segundo (que no coincide con el primero) como descubrió. Si agregara propiedades adicionales al primer objeto que coincidan con las utilizadas en el segundo, entonces vería ambos objetos.

Write-HostNo le importan este tipo de cosas. El rechazo al uso de esto tiene que ver principalmente con (1) que es una forma perezosa de dar retroalimentación sobre el progreso del script, es decir, usar Write-Verboseo Write-Debugen su lugar y (2) que es imperfecto cuando se trata de pasar objetos a lo largo de una tubería, etc.

Aclaración sobre el punto (2), planteada de manera útil en los comentarios a esta respuesta:

Write-Hostno solo es imperfecto con respecto a la canalización/redireccionamiento/captura de salida, simplemente no puedes usarlo para eso en PSv4 y versiones anteriores, y en PSv5+ tienes que usarlo de manera incómoda 6>; Además, Write-Hostse encadena con .ToString(), lo que a menudo produce representaciones inútiles.

Si su script en realidad solo está destinado a imprimir datos en la consola, continúe y Write-Host.

Alternativamente, puede devolver varios objetos desde un script o función. Usando returno Write-Output, simplemente devuelve objetos objeto separados por comas. Por ejemplo:

Prueba-WriteOutput.ps1

$object1 = [PSCustomObject]@{
  OSBuild = "910ef01.2.8779"
  OSVersion = "CustomOS 3"
  BIOSSerial = "A5FT-XT2H-5A4B-X9NM"
}

$object2 = [PSCustomObject]@{
  Site = "SQL site"
  Server= "SQL Server"
  Database="SQL Database"
}

Write-Output $object1,$object2

Luego ejecute el script, asignando la salida a dos variables:

$a,$b = .\Test-WriteOutput.ps1

Verás que $a es $object1 y $b es $object2 .

MisterSeajay avatar Oct 26 '2016 13:10 MisterSeajay