Windows PowerShellInforme de progreso

Don Jones

Recientemente escribí un script bastante largo y complicado de Windows PowerShell que, durante la ejecución, dejaba de responder casi por completo. Lo escribí para la ejecución como tarea programada, así que no producía prácticamente resultados visibles. Al ejecutar la primera prueba importante, me preocupó la idea de que,

accidentalmente, quizás había escrito un bucle infinito o algún otro tipo de script problemático.

Cuando vi que el shell permanecía inmóvil, con el parpadeo patético de su cursor, me pregunté: "¿He acabado con el shell?" Aparentemente, no tengo confianza en mí mismo porque presioné rápidamente las teclas Ctrl+C para interrumpir el script. Hora de hacer uso de un informe de progreso.

Tonterías, nada más que tonterías

Lo primero que quería hacer era agregar varios mensajes de estado para saber exactamente lo que hacía el script. El shell permite hacer esto fácilmente con el cmdlet Write-Verbose. Adelante; pruébelo en el shell:

Write-Verbose "Test Message"

Si lo ha probado, habrá advertido que no hace nada. Eso es porque Write-Verbose envía objetos a la canalización Verbose especial que, de forma predeterminada, no muestra su resultado. Una variable integrada del shell, $VerbosePreference, controla esta canalización. El valor predeterminado de esta variable es SilentlyContinue, que suprime los resultados detallados. La configuración en Continue abre la canalización:

$VerbosePreference = "Continue"

Ahora puedo agregar varias instrucciones Write-Verbose a mi script y obtener detalles sobre lo que pasa durante la ejecución. Y lo mejor de esta técnica es que, al acabar de probar y solucionar problemas, puedo eliminar todos los estorbos adicionales mediante la configuración de $VerbosePreference una vez más en SilentlyContinue al principio del script.

No hay necesidad de quitar todas las instrucciones Write-Verbose. De hecho, al permanecer en el script, cada vez que necesito ejecutar el script manualmente, puedo volver a activar fácilmente la canalización Verbose si es necesario.

Pero necesito progreso real.

Cuando quedé satisfecho de que el script no estaba atrapado en un bucle infinito y que, de hecho, funcionaba perfectamente, desactivé la canalización Verbose y lo ejecuté otra vez, para asegurarme.

El problema ahora es que, a pesar de saber que el script funciona perfectamente, no puedo soportar la vista de un cursor intermitente. Tengo problemas de concentración. Busqué desesperadamente algo de pintura; así, al menos, podría sentarme a mirar cómo se seca.

Lo que necesitaba era una indicación general del progreso del script y una idea aproximada de cuándo acabaría. Básicamente, quería una barra de progreso o algo así.

Afortunadamente, Windows PowerShellTM incluye el cmdlet Write-Progress. Este cmdlet no ofrece una barra de progreso gráfica como vemos en Windows®, pero produce una barra de progreso bastante decente, como muestra la Figura 1. Tiene un aspecto similar a la barra de progreso de copia de archivo que usa la parte basada en texto de la configuración en Windows Server ® 2003 o incluso en Windows XP.

Figura 1 ¿Cuál es el progreso del script?

Figura 1** ¿Cuál es el progreso del script? **(Hacer clic en la imagen para ampliarla)

El uso de Write-Progress requiere un poco de explicación. Pienso que un ejemplo sería aún mejor. Considere este script:

for ($a=1; $a -lt 100; $a++) {
  Write-Progress -Activity "Working..." `
   -PercentComplete $a -CurrentOperation
   "$a% complete" `
   -Status "Please wait."
  Start-Sleep 1
}

Usa Write-Progress para mostrar una barra de progreso. He usado Start-Sleep con el fin de interrumpir el script un segundo cada vez en el bucle para que se ejecute con la lentitud necesaria que nos permita ver el progreso: sin estas pausas, el bucle cuenta de 0 a 100 tan rápidamente que la barra de progreso sólo se ilumina brevemente en la pantalla.

Como puede ver, Activity, que he establecido en Working, se muestra en la parte superior de la barra de progreso. Status se muestra justo debajo, y CurrentOperation aparece en la parte inferior. El shell sólo es compatible con una barra de progreso cada vez. Cada uso de Write-Progress creará una barra de progreso nueva, si no se muestra una actualmente, o actualiza la barra actual.

Lo que no he hecho aquí es indicar a la barra que debe desaparecer en cuanto termine. Puedo hacerlo al agregar esto al final del script:

Write-Progress -Activity "Working..." `
 -Completed -Status "All done."

Por lo general, la barra de progreso desaparecerá por sí sola una vez que ha finalizado el script pero, si el script tiene otras cosas que hacer, deseará ocultar la barra de progreso cuando ya no la necesite. El parámetro -Completed quita simplemente la barra de la pantalla.

Tick, Tick, Tick

Otro uso común de Write-Progress es mostrar los segundos de ejecución que quedan, en lugar de una barra de progreso. Por ejemplo:

for ($a=100; $a -gt 1; $a--) {
  Write-Progress -Activity "Working..." `
   -SecondsRemaining $a -CurrentOperation
   "$a% complete" `
   -Status "Please wait."
  Start-Sleep 1
}

Todo lo que he hecho aquí es cambiar el recuento del bucle de 100 a 1, y he usado el parámetro SecondsRemaining de Write-Progress, en lugar de PercentComplete. El resultado aparece en la figura 2. Como puede ver, el indicador de progreso ha desaparecido; ha quedado reemplazado por un reloj de cuenta atrás. El shell convierte automáticamente el número total de segundos que quedan en horas, minutos y segundos, lo que simplifica la información. El porcentaje completado que se muestra aquí cuenta hacia atrás desde 100, lo que corresponde al parámetro CurrentOperation que he proporcionado. No se produce el cálculo real del porcentaje completado; Windows PowerShell muestra simplemente el valor de $a seguido de la cadena "% complete".

Figura 2 ¿Cuánto queda para que se complete el script?

Figura 2** ¿Cuánto queda para que se complete el script? **(Hacer clic en la imagen para ampliarla)

Scripts capaces de comunicación

Me encanta escribir scripts que comunican lo que andan haciendo, ya sea en formato de resultados detallados o mediante una barra de progreso sencilla. Esto puede beneficiarme a mí, que tengo problemas de concentración, o a cualquiera que necesite ejecutar mi script dentro de unos meses. En última instancia, mostrar algún tipo de información de estado y progreso será una gran ventaja.

Cmdlet del mes: Tee-Object

Este mes, quiero echar un vistazo a uno de mis cmdlets de solución de problemas favoritos. Veamos este ejemplo:

Get-WMIObject Win32_Service | Where { $_.State -ne "Running"
-and $_.StartMode -eq "Automatic" } | ForEach-Object { $_.Start() }

A simple vista, parece que esto iniciaría todos los servicios que están establecidos para el inicio automático; por alguna razón, todavía no se han iniciado. Sin embargo, esto no funciona realmente, y averiguar por qué no funciona puede ser complicado, ya que no podemos ver lo que ocurre dentro de la canalización. Bueno, no podemos ver lo que ocurre dentro de la canalización a menos que usemos Tee-Object.

Tee-Object redirecciona objetos a un archivo (o a una variable) y los pasa por la canalización. Por ejemplo:

Get-WMIObject Win32_Service | Tee-Object AllServices.csv | Where 
{ $_.State -ne "Running" -and $_.StartMode -eq "Automatic" } | 
Tee-Object FilteredServices.csv | ForEach-Object { $_.Start() }

Esta modificación me permite ver lo que sucede después de cada comando de canalización. Así, descubro rápidamente que mi archivo FilteredServices.csv no contiene nada. No es de extrañar que el script no funcione. Al investigar un poco más, vemos la causa raíz del problema: StartMode es "Auto", no "Automatic". Tee-Object me permite localizar con toda precisión el lugar exacto del problema.

Don Jones es editor colaborador de la revista TechNet Magazine y coautor de Windows PowerShell: TFM (SAPIEN Press, 2007). Se dedica a enseñar Windows PowerShell (www.ScriptingTraining.com) y puede ponerse en contacto con él a través del sitio web ScriptingAnswers.com.

© 2008 Microsoft Corporation and CMP Media, LLC. Reservados todos los derechos; queda prohibida la reproducción parcial o total sin previa autorización.