PowerShell: la salida de escritura solo escribe un objeto
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.
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-Table
se 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.
- Al enviar a la consola , si el primer objeto tiene formato de tabla (si
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-Host
es preferible Write-Host
y por qué es mejor evitarlo Write-Host
en 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-String
es 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-Output
no ayuda:
Write-Output
no 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-Output
rara 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-Output
está 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-Host
Por qué no es aconsejable el uso de , tanto aquí como a menudo en general:
Suponiendo que conoce las implicaciones del uso Write-Host
en general (ver más abajo), podría usarlo para el problema en cuestión, pero Write-Host
aplica 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-Host
Hice 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) esSystem.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-Host
es para escribir resultados (datos), pero no es así.
- Como principiante, puedes pensar erróneamente que
Al omitir el sistema de flujos de PowerShell,
Write-Host
la 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 nuevoWrite-Information
cmdlet.
Por el contrario,Out-Host
la salida todavía no se puede redirigir.
- 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
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-ForegroundColor
y-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 deRead-Host
).De manera similar, puede utilizar
Write-Host
coloració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-Verbose
enWrite-Debug
tales casos.
- Sin embargo, generalmente es mejor utilizarlo
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-Process
devuelve 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-Process
lo 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-Host
No 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-Verbose
o Write-Debug
en 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-Host
no 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ómoda6>
; Además,Write-Host
se 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 return
o 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 .