Deje de usar Write-Host — Flujos de salida de PowerShell y diseño de registros

· Actualizado el: · · PowerShell, Windows, Registros, Mejora operativa, Automatización, Scripts, Diseño, Mantenibilidad

«El script funciona, pero cuando falla no sabemos qué pasó» — esta es la consulta más frecuente sobre scripts de PowerShell puestos en producción. Y cuando se investiga la causa, casi siempre se llega a la misma estructura: el estado del proceso solo se expresaba mediante Write-Host, y en una ejecución nocturna sin nadie mirando la pantalla no queda ningún rastro.

PowerShell dispone de seis tipos de flujos de salida, y cada uno tiene un destinatario distinto: si devuelve un valor, si está pensado para que lo lea una persona, o si solo hace falta durante una investigación. Si escribe el código teniendo esto en cuenta, el mismo script se comporta de forma amable en ejecución interactiva y de forma legible por máquina en ejecución desatendida. Por el contrario, si vuelca todo en Write-Host, solo aumenta la información que ni sirve como valor ni queda registrada.

En este artículo repasamos el papel de los seis flujos, la posición correcta de Write-Host, el mecanismo por el que se contamina el valor de retorno de una función, la forma de controlar el nivel de detalle desde el llamador y, por último, un formato de registro estructurado útil en producción.

1. Antes que nada, la conclusión

  • Los flujos de salida de PowerShell son seis. Éxito (1), error (2), advertencia (3), detallado (4), depuración (5) e información (6); cada uno puede redirigirse por su número. *> representa todos los flujos.1
  • Desde PowerShell 5.0, Write-Host escribe en el flujo de información. Por eso ahora es posible redirigirlo con 6> o capturarlo con -InformationVariable. Antes de esa versión no se podía ni capturar ni suprimir.2
  • Write-Host está pensado únicamente para «mostrar algo a una persona». No sirve para devolver valores. El valor que se pasa a la canalización (pipeline) se escribe con Write-Output (o una salida sin comando).23
  • Una función devuelve todos los objetos que se generan en su interior, tenga o no una sentencia return. La práctica habitual para descartar salidas no deseadas es $null = ....4
  • El progreso se muestra con Write-Progress, y el avance del proceso con Write-Verbose. La visualización de progreso no es un flujo de datos redirigible.5
  • Una función con [CmdletBinding()] obtiene automáticamente parámetros comunes como -Verbose, -Debug o -InformationAction. Lo correcto es diseñarla para que sea el llamador quien decida el nivel de detalle.67
  • Vale la pena recordar los valores predeterminados. $VerbosePreference, $DebugPreference e $InformationPreference valen SilentlyContinue; $WarningPreference y $ErrorActionPreference valen Continue.8
  • Si quiere analizar los registros más adelante, estructúrelos (un JSON por línea). Si además quiere reproducir el aspecto de la pantalla, combine esto con Start-Transcript.9

La versión de referencia de este artículo y las diferencias en 5.1

En muchos departamentos de sistemas, Windows PowerShell 5.1 sigue siendo la versión predominante. Antes de continuar, aclaremos en qué versión se basa cada afirmación de este artículo.

Descripción Versión de referencia En Windows PowerShell 5.1
Los seis flujos de salida y la redirección numerada, *> Igual en 5.1 y en PowerShell 7 Se aplica sin cambios1
Write-Host escribe en el flujo de información (número 6) y puede capturarse con 6> o -InformationVariable Desde PowerShell 5.0 (capítulo 3) 5.1 es posterior a 5.0, así que se aplica sin cambios2
Write-Information y -InformationAction / -InformationVariable Desde PowerShell 5.0 (capítulo 4) Se aplica sin cambios7
Que la función devuelva todo lo que genera internamente y la supresión con $null = ... No depende de la versión (capítulo 5) Se aplica sin cambios4
El tratamiento de 2>&1 en comandos externos (comandos nativos) Se describe como el comportamiento de PowerShell 7.4 en adelante (capítulo 6) No se aplica. Compruebe siempre, en la versión que va a ejecutar, cómo se clasifica por tipo la salida de comandos externos
Controlar Write-Progress con el parámetro común -ProgressAction Desde PowerShell 7.45 No está disponible. Se controla con $ProgressPreference (el capítulo 8 está escrito con este enfoque)
El código de ejemplo distribuido al final del artículo Verificado ejecutándolo en PowerShell 7.6 (14 pruebas de Pester)

En resumen, los capítulos 2 a 5 y el capítulo 7 se pueden usar tal cual en 5.1. Los dos únicos puntos donde hay que prestar atención a la versión son la redirección de comandos externos (capítulo 6) y el método de control de la visualización de progreso (capítulo 8).

2. Los seis flujos y sus destinatarios

Empecemos con un panorama de qué comando de escritura llega a dónde.

Write-Output / salida sin comandoWrite-Error / Write-WarningWrite-Verbose / Write-DebugWrite-Information / Write-Host1 Flujo de éxito2 Error / 3 Advertencia / 4 Detallado5 Depuración / 6 InformaciónPasa al procesamiento posteriorCanalización o asignación a variableAparece en pantallaQue aparezca por defecto depende de la variable de preferenciaSe puede conservar en un archivoRedirección numerada o variable de captura

Solo el flujo de éxito pasa al procesamiento posterior. Los otros cinco, o aparecen en pantalla, o se capturan mediante redirección o parámetros -*Variable, pero nunca entran en la canalización. Si se equivoca en la bifurcación de la izquierda (con qué comando escribir), el error siempre acaba manifestándose: el valor no llega, o no queda registrado. Los números se usan para especificar la redirección, como en 3> warnings.log (capítulo 6).

N.º Flujo Comando que escribe Destinatario previsto Preferencia predeterminada
1 Éxito (Success) Write-Output / salida sin comando El procesamiento posterior (canalización)
2 Error (Error) Write-Error / lanzar una excepción Persona + monitorización Continue
3 Advertencia (Warning) Write-Warning Persona Continue
4 Detallado (Verbose) Write-Verbose Persona que está investigando SilentlyContinue
5 Depuración (Debug) Write-Debug Persona desarrolladora SilentlyContinue
6 Información (Information) Write-Information / Write-Host Persona + registro SilentlyContinue

Que el valor predeterminado del flujo de información sea SilentlyContinue y que aun así Write-Host aparezca en pantalla no es una contradicción. Write-Host es la única excepción: Microsoft Learn indica expresamente que «la variable de preferencia $InformationPreference y el parámetro común -InformationAction no afectan a los mensajes de Write-Host».2 Es decir, Write-Information no aparece por defecto, pero Write-Host sí se muestra incluso por defecto, y solo se puede suprimir con -InformationAction Ignore o mediante la redirección 6>.

Lo más importante de esta tabla es la primera fila. El flujo de éxito no es el lugar para escribir «mensajes dirigidos a personas». Si escribe ahí una cadena pensada para que la lea alguien, en el momento en que encadene esa función con |, un texto inesperado se colará en el procesamiento posterior.

function Get-KsTargetFile {
    Write-Output "Buscando los archivos objetivo..."   # [MAL] se mezcla con el valor de retorno
    Get-ChildItem -Path $path -Filter '*.csv'
}

# El llamador espera un arreglo de FileInfo, pero al principio llega una cadena
$files = Get-KsTargetFile
$files[0].FullName    # → vacío (porque el primer elemento es una cadena)

Lo correcto es enviar los informes de progreso al flujo detallado o al flujo de información.

function Get-KsTargetFile {
    [CmdletBinding()]
    param([string] $Path)

    Write-Verbose "Buscando los archivos objetivo: $Path"   # solo se muestra si se indica -Verbose
    Get-ChildItem -Path $Path -Filter '*.csv'                # el valor de retorno es únicamente FileInfo
}

3. ¿Es Write-Host «malo»?

En su momento se popularizó la afirmación de que «no hay que usar Write-Host», pero en el PowerShell actual la situación ha cambiado. Desde PowerShell 5.0, Write-Host está implementado como una escritura en el flujo de información (número 6), por lo que se puede redirigir con 6> o capturar con -InformationVariable.2 La crítica de entonces, «solo se puede mostrar en pantalla y no se puede recuperar después», ya no es válida. Sin embargo, como se mencionó en el capítulo 2, aunque el valor predeterminado del flujo de información sea SilentlyContinue, la visualización de Write-Host no desaparece por eso: no le afectan $InformationPreference ni -InformationAction (la única excepción es -InformationAction Ignore, que sí suprime también la salida de Write-Host). Que «ahora se pueda capturar» y que «aparezca en pantalla por defecto» son dos cosas compatibles.2

Aun así, su uso está limitado a casos concretos.

Situaciones en las que Write-Host es adecuado

  • En herramientas de uso interactivo, cuando quiere mostrar títulos o separadores en color (-ForegroundColor)
  • Cuando quiere comunicar al usuario «qué va a hacer este script a continuación»
  • Cuando el objetivo no es un valor del proceso, sino la propia presentación decorativa

Situaciones en las que no debe usar Write-Host

  • Cuando quiere pasar un valor como retorno de una función (→ Write-Output)
  • Cuando quiere dejar un registro operativo que se analizará después (→ registro estructurado, o Write-Information)
  • Cuando quiere que el llamador pueda alternar entre mostrar u ocultar el mensaje (→ Write-Verbose)

En un script de ejecución desatendida, para empezar no hay ningún destino donde mostrar nada. Un script que expresa su estado únicamente con Write-Host se convierte, en el momento en que se ejecuta desde el Programador de tareas, en «un script del que no se sabe nada». Ese es el sentido del título de este artículo.

4. Delegar el control al llamador — [CmdletBinding()] y los parámetros comunes

El verdadero valor de Write-Verbose está en que es el llamador quien decide si se muestra o no. Basta con añadir [CmdletBinding()] a la función para que queden disponibles automáticamente parámetros comunes como -Verbose, -Debug, -WarningAction, -InformationAction o -ErrorAction.67

function Invoke-KsImport {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [string] $CsvPath
    )

    Write-Verbose "Inicio de la importación: $CsvPath"          # no aparece por defecto
    Write-Information "Importación: $CsvPath" -Tags 'KsImport'  # no aparece por defecto (pero se puede capturar)

    $rows = Import-Csv -Path $CsvPath
    if ($rows.Count -eq 0) {
        Write-Warning "$CsvPath no tiene nada que importar"   # aparece por defecto
        return
    }

    Write-Verbose "Se procesarán $($rows.Count) filas"
    $rows | ForEach-Object { ConvertTo-KsRecord $_ }          # esto es lo único que se devuelve
}

# Ejecución normal: solo se muestra la advertencia, y el retorno son los registros
$records = Invoke-KsImport -CsvPath 'D:\in\orders.csv'

# Durante una investigación: también se quiere ver el avance
$records = Invoke-KsImport -CsvPath 'D:\in\orders.csv' -Verbose

# Se captura solo el flujo de información en una variable, para volcarlo luego a un archivo de registro
$records = Invoke-KsImport -CsvPath 'D:\in\orders.csv' -InformationVariable info
$info | ForEach-Object { $_.MessageData } | Add-Content -Path $logPath

Es habitual encontrarse implementaciones que crean su propia variable $LogLevel y ramifican con sentencias if, pero apoyarse en el mecanismo estándar resulta más breve y comunica mejor la intención a quien lo lea. Que al añadir -Verbose aparezca el detalle es un conocimiento compartido por cualquiera que use PowerShell.

Por cierto, variables de preferencia como $VerbosePreference afectan al ámbito actual y a los ámbitos hijos.8 Si llama a una función con -Verbose, los cmdlets que se invocan dentro de ella también empiezan a producir salida detallada, por lo que a veces obtiene más salida de la esperada.

5. Cuando se contamina el valor de retorno de una función — una trampa propia de PowerShell

Las funciones de PowerShell devuelven todos los objetos generados en su interior, aunque no exista una sentencia return explícita.4 Se trata de una característica muy potente, pero también es la que más incidentes provoca.

function New-KsWorkFolder {
    param([string] $Path)

    New-Item -Path $Path -ItemType Directory   # [TRAMPA] el DirectoryInfo se mezcla en el retorno

    $list = [System.Collections.Generic.List[string]]::new()
    $list.Add('log')                            # [TRAMPA 2] .Add() devuelve void, así que no causa daño
    $sb = [System.Text.StringBuilder]::new()
    $sb.Append('x')                             # [TRAMPA 3] el propio StringBuilder se devuelve

    return $Path
}

$p = New-KsWorkFolder -Path 'D:\work'   # $p termina siendo un arreglo de 3 elementos (DirectoryInfo, StringBuilder, string)

La solución consiste en «descartar la salida no deseada». Hay tres formas de escribirlo, pero $null = ... es la más ligera.

$null = New-Item -Path $Path -ItemType Directory    # recomendado
New-Item -Path $Path -ItemType Directory | Out-Null # más lento por el uso de la canalización
[void] $sb.Append('x')                              # se usa a menudo con métodos de .NET

Este comportamiento se detecta de inmediato en cuanto se escribe una prueba. La importancia de las pruebas que «fijan la forma del valor de retorno» se trata en «Organización de pruebas de PowerShell con Pester».

6. Redirección y captura

Los flujos se pueden redirigir individualmente por número.1

.\Invoke-NightlyExport.ps1 3> warnings.log                      # solo las advertencias a un archivo aparte
.\Invoke-NightlyExport.ps1 4>&1 | Tee-Object -FilePath run.log  # fusiona el flujo detallado con el de éxito
.\Invoke-NightlyExport.ps1 *> all.log                            # todos los flujos a un único archivo
.\Invoke-NightlyExport.ps1 2>&1 | Where-Object { $_ -is [System.Management.Automation.ErrorRecord] }

> sobrescribe y >> añade al final. Ahora bien, tenga presente que al fusionar flujos mediante redirección, los tipos se mezclan. En el ejemplo anterior, al fusionar el flujo de error de un script o función de PowerShell, ese elemento sigue siendo un ErrorRecord, así que se puede clasificar por tipo tal como se muestra arriba.

En cambio, 2>&1 con un programa externo (comando nativo) es harina de otro costal. A partir de PowerShell 7.4, la salida redirigida se trata como flujo de bytes, y tras la fusión se convierte en cadenas de texto, por lo que la clasificación por ErrorRecord ya no funciona. Si necesita distinguir stdout de stderr en un comando externo, recíbalos por separado en lugar de fusionarlos (véase «Cómo invocar correctamente un exe externo desde PowerShell»).

La forma más segura de saber si la separación funciona como espera es probarla usted mismo una vez. Puede comprobarlo con un fragmento corto que genere salida tanto en el flujo de éxito como en el de advertencia.

# Solo la advertencia va a un archivo aparte. En pantalla solo queda 'Datos'
& { Write-Output 'Datos'; Write-Warning 'Aviso' } 3> warnings.log
Get-Content warnings.log     # contiene el mensaje de advertencia; 'Datos' no aparece

# Todos los flujos a un único archivo
& { Write-Output 'Datos'; Write-Warning 'Aviso'; Write-Verbose 'Detalle' -Verbose } *> all.log
Get-Content all.log          # contiene las tres salidas juntas

Si a pesar de usar 3> el archivo warnings.log está vacío, es que ese mensaje no se escribió en el flujo de advertencia (si se escribió con Write-Host, es 6>). La forma más rápida de diagnosticar «esperaba que apareciera en el registro y no aparece» es empezar por estas dos líneas.

Por cierto, *> es cómodo, pero al juntarlo todo, luego resulta difícil volver a separarlo por flujo. Si quiere agregarlo de forma automática, escriba cada flujo en un archivo distinto, o use el registro estructurado del siguiente capítulo.

Si quiere conservar por completo el rastro de la ejecución, Start-Transcript es una opción sencilla. Registra en un archivo de texto los comandos y la salida de la sesión, de modo que después puede reproducir «qué había en pantalla en aquel momento».9

Start-Transcript -Path "C:\Logs\export_$(Get-Date -f yyyyMMdd_HHmmss).log" -Append
try   { Invoke-KsExport }
finally { Stop-Transcript }

7. Registros que se puedan analizar más tarde — registros estructurados

Un registro pensado para que lo lea una persona y otro pensado para que lo agregue una máquina son cosas distintas. Cuando quiere saber «cuántas veces apareció este error el mes pasado», un texto de formato libre se convierte en una competencia de resistencia con grep. Si lo escribe como un JSON por línea (JSON Lines), la agregación se resuelve enteramente con PowerShell.

function Write-KsLog {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [ValidateSet('INFO','WARN','ERROR')] [string] $Level,
        [Parameter(Mandatory)] [string] $Message,
        [hashtable] $Data,
        [string] $Path = $script:KsLogPath
    )

    $entry = [ordered]@{
        ts      = (Get-Date).ToString('o')    # ISO 8601: facilita ordenar y cotejar
        level   = $Level
        message = $Message
        script  = $MyInvocation.ScriptName
        host    = $env:COMPUTERNAME
        user    = $env:USERNAME
    }
    # La información adicional se guarda bajo data. Si se mezclara en el nivel
    # superior, cuando el llamador pasara claves como level o user, sobrescribiría
    # los campos básicos
    if ($Data) { $entry['data'] = $Data }

    # -Compress lo deja en una sola línea. Para añadir se usa Add-Content (UTF-8)
    $entry | ConvertTo-Json -Compress -Depth 5 | Add-Content -Path $Path -Encoding utf8

    # La visualización para personas se apoya en el mecanismo estándar
    # (aparece en pantalla pero no devuelve ningún valor)
    switch ($Level) {
        # No se especifica -ErrorAction. Si se fijara aquí, el llamador no
        # podría usar -ErrorAction Stop para convertirlo en un error terminante
        'ERROR' { Write-Error   $Message }
        'WARN'  { Write-Warning $Message }
        default { Write-Verbose $Message }
    }
}

# Ejemplo de uso
Write-KsLog -Level INFO -Message 'Importación completada' -Data @{ rows = 1250; file = 'orders.csv'; ms = 4210 }
# → {"ts":"...","level":"INFO","message":"Importación completada","script":"...","host":"...","user":"...",
#    "data":{"rows":1250,"file":"orders.csv","ms":4210}}

La agregación quedaría así.

Get-Content 'C:\Logs\ks.log' |
    ForEach-Object { $_ | ConvertFrom-Json } |
    Where-Object { $_.level -eq 'ERROR' -and [datetime]$_.ts -ge (Get-Date).AddDays(-30) } |
    Group-Object message | Sort-Object Count -Descending | Select-Object Count, Name

También existe la opción de apoyarse en la infraestructura estándar de registro de Windows (el registro de eventos y ETW). Si piensa en integrarse con herramientas de monitorización o en recopilar datos de varios equipos, esa opción resulta más ventajosa. La comparación de diseño se recoge en «Registro de eventos y ETW de Windows frente a registros estructurados», y la gestión de generaciones de archivos de registro en «PowerShell aplicado — investigación, archivado y generación de informes de registros».

8. Cómo tratar la visualización de progreso

Write-Progress utiliza la función de visualización de progreso del host, y no es un flujo de datos redirigible.5 Es decir, no se puede conservar en un registro. En una ejecución desatendida no hay ningún lugar donde mostrarlo, y además, según el entorno, el coste de actualizar el progreso puede no ser despreciable.

# Desactiva la visualización de progreso al principio de un script de ejecución desatendida
$ProgressPreference = 'SilentlyContinue'

Si quiere dejar constancia del progreso en el registro operativo, resulta más práctico escribir en el flujo detallado solo los hitos.

$i = 0
foreach ($row in $rows) {
    $i++
    if ($i % 100 -eq 0) { Write-Verbose "$i / $($rows.Count) filas completadas" }
    ...
}

9. Reglas prácticas de uso habitual (tabla de decisión)

Información que quiere mostrar Qué usar Motivo
Valor que se pasa al procesamiento posterior Write-Output / salida sin comando El flujo de éxito es exclusivo para datos3
Avance del proceso (solo se quiere ver al investigar) Write-Verbose El llamador lo controla con -Verbose7
Suceso que se quiere registrar como parte de la operación Write-Information + registro estructurado Se puede capturar con -InformationVariable2
Presentación decorativa de una herramienta interactiva Write-Host Pasa por el flujo de información, así que también se puede capturar2
Situación prevista pero que merece atención Write-Warning Se muestra por defecto y se captura con -WarningVariable8
Fallo Write-Error / throw Consulte el artículo dedicado sobre gestión de errores
Estado interno durante el desarrollo Write-Debug Solo cuando se añade -Debug7
Progreso Write-Progress (solo en modo interactivo) No queda en el registro. Desactívelo en ejecución desatendida5
Todo el rastro de la ejecución Start-Transcript Úselo junto con su propio registro, como respaldo9

10. Resumen

  • La salida de PowerShell se divide en seis flujos. El flujo de éxito es exclusivo para datos; si mezcla mensajes dirigidos a personas, rompe el valor de retorno.
  • Desde PowerShell 5.0, Write-Host escribe en el flujo de información, así que se puede capturar, pero no sirve para devolver valores ni para el registro operativo.
  • Una función devuelve todo lo que genera en su interior. La práctica habitual es descartar la salida no deseada con $null = ....
  • Si añade [CmdletBinding()] y usa Write-Verbose / Write-Information, puede delegar el control del nivel de detalle al llamador. Es más breve que una variable de nivel de registro propia y comunica mejor la intención.
  • Si va a agregar el registro más adelante, hágalo como registro estructurado con un JSON por línea. Si el objetivo es reproducir lo que apareció en pantalla, combínelo con Start-Transcript.
  • La visualización de progreso no es un flujo de datos, así que no queda en el registro. En ejecución desatendida, lo más práctico es desactivarla con $ProgressPreference = 'SilentlyContinue'.

Descarga del código de ejemplo

El código tratado en este artículo se distribuye ya empaquetado y listo para ejecutarse. Incluye el registro estructurado con un JSON por línea y la forma de escribir funciones que no contaminan el valor de retorno.

Descargar el código de ejemplo (zip)

Los ejemplos de este artículo se han verificado ejecutándolos realmente en PowerShell 7.6 (14 pruebas de Pester). Si ejecuta Invoke-SampleTests.ps1, incluido en el zip, podrá reproducir la misma verificación en su propio equipo.

# Análisis de sintaxis + análisis estático + pruebas de Pester
./Invoke-SampleTests.ps1

Los valores de configuración (rutas, nombres de servidor, ID de inquilino, etc.) son solo ejemplos. No los ejecute tal cual en un entorno de producción: adáptelos al entorno de su organización.

Artículos relacionados

Áreas de consultoría relacionadas

KomuraSoft LLC se ocupa de revisar el diseño de registros de scripts operativos, de resolver la situación de «no saber por qué falló» y de preparar la infraestructura de registro que conecta con la monitorización y la agregación.

Referencias

  1. Microsoft Learn, about_Redirection. Sobre los flujos de éxito, error, advertencia, detallado, depuración e información de PowerShell, identificados cada uno por un número; la redirección a archivo con > y >>; la fusión con otro flujo mediante n>&1; y la redirección de todos los flujos con *> 2 3

  2. Microsoft Learn, Write-Host. Sobre cómo, desde PowerShell 5.0, Write-Host pasó a funcionar como envoltorio de Write-Information que escribe en el flujo de información, lo que permitió redirigirlo con 6>; sobre que la variable de preferencia $InformationPreference y el parámetro común -InformationAction no afectan a los mensajes de Write-Host (la excepción es -InformationAction Ignore), por lo que se muestra en pantalla incluso por defecto; sobre la decoración con -ForegroundColor / -BackgroundColor; y sobre que la salida no se pasa a la canalización. En relación con esto, Write-Information trata la escritura explícita en el flujo de información y su clasificación mediante -Tags.  2 3 4 5 6 7 8

  3. Microsoft Learn, Write-Output. Sobre el envío de objetos al flujo de éxito (la canalización), y sobre que el resultado de una expresión se genera igual aunque no se llame explícitamente.  2

  4. Microsoft Learn, about_Return. Sobre que las funciones de PowerShell devuelven al llamador todos los objetos generados en su interior, tengan o no una sentencia return, y sobre que return es la sintaxis para devolver un valor y salir del ámbito actual a la vez.  2 3

  5. Microsoft Learn, Write-Progress. Sobre cómo se muestra el avance de un comando mediante la visualización de progreso del host, sobre el control de esa visualización con $ProgressPreference, y sobre que desde PowerShell 7.4 también se puede controlar con el parámetro común -ProgressAction.  2 3 4

  6. Microsoft Learn, about_Functions_CmdletBindingAttribute. Sobre cómo una función avanzada con el atributo [CmdletBinding()] se comporta igual que un cmdlet compilado, y sobre la disponibilidad automática de los parámetros comunes.  2

  7. Microsoft Learn, about_CommonParameters. Sobre el comportamiento de -Verbose / -Debug / -WarningAction / -InformationAction / -ErrorAction y de los parámetros -*Variable correspondientes, y su relación con las variables de preferencia.  2 3 4 5

  8. Microsoft Learn, about_Preference_Variables. Sobre que el valor predeterminado de $VerbosePreference, $DebugPreference e $InformationPreference es SilentlyContinue, y el de $WarningPreference y $ErrorActionPreference es Continue; sobre el control de la visualización de progreso mediante $ProgressPreference; y sobre que estas variables se aplican al ámbito actual y a los ámbitos hijos.  2 3

  9. Microsoft Learn, Start-Transcript. Sobre el registro en un archivo de texto de los comandos y la salida de consola de la sesión, sobre añadir al final con -Append, y sobre detener el registro con Stop-Transcript.  2 3

Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.

Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.

El artículo está directamente relacionado con los siguientes servicios.

Preguntas frecuentes

Preguntas habituales en las consultas sobre el tema del artículo.

¿No se debe usar Write-Host?
No es que esté prohibido, sino que su uso es limitado; esa es la interpretación correcta. Desde PowerShell 5.0, Write-Host escribe en el flujo de información (número 6), lo que permite redirigirlo con 6> o capturarlo con -InformationVariable. Aun así, es un comando que parte de la premisa de mostrarse siempre en pantalla por defecto, y no sirve para hacer fluir un valor por la canalización. La distinción de uso queda así: Write-Host para presentaciones decorativas que va a leer una persona en una herramienta interactiva; Write-Verbose o Write-Information para el avance del proceso o información complementaria; y Write-Output (o una salida sin comando) para el valor que se pasa al procesamiento posterior.
El valor de retorno de mi función se mezcla con valores que no esperaba.
Es porque las funciones de PowerShell devuelven todos los objetos generados en su interior, aunque no exista una sentencia return explícita. Si invoca sin capturar comandos o métodos que devuelven un valor, como New-Item o el .Append() de StringBuilder, ese valor de retorno fluye por el flujo de éxito y llega al llamador. En cambio, los métodos cuyo retorno es void, como el .Add() de List[T], no generan ninguna salida, así que no hace falta suprimirlos. La solución consiste en descartar la salida no deseada con $null = ..., añadir | Out-Null, o convertirla con [void]. En cuanto a rendimiento, $null = ... es la forma más ligera de escribirlo.
Quiero poder activar o desactivar el registro detallado de un script en tiempo de ejecución.
Escriba el avance intermedio del proceso con Write-Verbose y añada [CmdletBinding()] a la función. Con eso basta para que solo se muestre cuando el llamador indica -Verbose. Si quiere que se muestre siempre, establezca $VerbosePreference = 'Continue' al principio del script. De la misma manera, Write-Debug se controla desde el llamador con -Debug, y Write-Warning con -WarningAction. En lugar de crear su propia variable de nivel de registro, apoyarse en el mecanismo estándar de PowerShell comunica mejor la intención a quien lea el código después.
¿Cómo puedo conservar en un archivo todo, incluidos el registro detallado y las advertencias?
Hay tres formas según el uso que le quiera dar. Si simplemente quiere volcar todos los flujos a un archivo, redirija con *>. Si quiere conservar por completo, como rastro de la ejecución, lo que apareció en pantalla, Start-Transcript resulta sencillo. Si quiere analizarlo más adelante mediante un programa, lo más seguro es escribir un registro estructurado de un JSON por línea con su propia función de registro, y en ese caso vale la pena combinarlo también con la transcripción, como respaldo.
¿Se puede conservar en un archivo de registro lo que muestra Write-Progress?
No se puede. La visualización de progreso es una función de presentación del host y se trata de forma distinta a los flujos de datos redirigibles. En una ejecución desatendida no hay ningún destino donde mostrarlo, así que conviene tratar el progreso por separado de la información que se conserva en el registro. En ejecución desatendida, detener la propia visualización de progreso con $ProgressPreference = 'SilentlyContinue' puede incluso acelerar de forma notable el proceso, según el entorno. Si quiere dejar constancia del progreso en el registro, resulta más práctico escribir con Write-Verbose solo los hitos, como «completadas 50 de 100 filas».

Perfil del autor

Página de presentación del autor del artículo.

Go Komura

Representante de KomuraSoft LLC

Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.

Volver al blog