El Blog de Gualtrysoft

Windows 2000/2003/2008, Active Directory, VBScript, Hyper-V, PowerShell y todo aquello interesante a la hora de usar, configurar y administrar Windows Server. También tenemos longanizas…

PowerShell Tip: Mostrar el Progreso con Texto en un Bucle

Posted by urpiano en Miércoles 12 \12\UTC diciembre \12\UTC 2012

Al hilo de este anterior Tip, en este otro vamos a ver cómo poder mostrar el progreso en un bucle en el que estamos procesando "algo" y queremos mostrar alguna propiedad de ese "algo" en el momento en que lo estamos procesando, lo que nos permitirá ver en qué objeto estamos en un determinado momento y que el proceso progresa.

Vamos a ver ésto con los procesos que se están ejecutando en el equipo, utilizando el Cmdlet Get-Process para listarlos. Veamos este código:

Function Get-ProcessProgress
{
    Write-Host "Procesando el proceso " -NoNewline
    $Anterior = ""
    $Previo = ""
    Get-Process -WarningAction SilentlyContinue | ForEach{ `
        $Incremento = $Previo.Length - $Anterior.Length
        If($Incremento -lt 0){$Incremento = 0}
        $Retroceso = "`b" * ($Anterior.Length + $Incremento)
        $Previo = $Anterior
        $Anterior = $_.Name
        $Incremento = $Previo.Length - $Anterior.Length
        If($Incremento -lt 0){$Incremento = 0}
        $Borrado = " " * $Incremento
        Write-Host "$Retroceso$Anterior$Borrado" -NoNewline
        Start-Sleep -Milliseconds 100
    }
    ""
}

La anterior funcion, al ser invocada, lista los procesos que se están ejecutando en el equipo; en cada uno de ellos, escribe el nombre del proceso y espera durante 100 milisegundos, para que así podamos ver algo. Por supuesto, la finalidad de esta función es realizar esto exclusivamente, pero imaginemos que tenemos que realizar determinadas tareas con cada uno de los procesos; sería tan simple como realizar estas tareas dentro del bucle, después de que se muestre en qué proceso nos encontramos, para que así supieramos que el proceso se está realizando y en qué proceso concreto se encuentra. Explicaremos a continuación qué hace este código.

En primer lugar, imprimimos el "Procesando el proceso " que precederá a cada uno de los nombres de proceso, utilizando -NoNewLine, lo que provoca que Write-Host no de un salto de línea después de escribir el texto. A continuación iniciaremos dos variables de cadena como cadena vacía: $Anterior y $Previo. Estas dos variables serán las encargadas de almacenar el nombre del proceso que fue procesado antes del actual ($Anterior) y el que fue procesado antes de éste ($Previo); veremos más adelante para qué.

    Write-Host "Procesando el proceso " -NoNewline
    $Anterior = ""
    $Previo = ""

A continuación listamos los procesos del equipo y, por canalización, los recorremos en un bucle ForEach:

    Get-Process -WarningAction SilentlyContinue | ForEach{ `

A continuación calculamos el "incremento" ¿Qué es este "incremento"? Es la diferencia que hay entre la longitud del nombre del proceso previo al anterior y la longitud del proceso anterior y se utiliza para agregar tantos retrocesos como espacios en blanco hubo que escribir después del nombre del proceso anterior. No sólo hay que realizar tantos retrocesos como longitud tenía el nombre del anterior proceso, si no que, como puede tener más longitud el nombre del anterior que el nombre actual y el caracter especial de retroceso no borra, habrá que escribir después del nombre tantos espacios como caracteres sobran del nombre anterior. Esto obliga también a mirar si el nombre previo al anterior tenía más longitud que el nombre anterior, para añadir tantos retrocesos como espacios se incluyeron; estos retrocesos que se añaden es el incremento que almacenaremos en la variable $Incremento. Veamos un ejemplo:

  • Estamos en el proceso lsm.exe y el anterior fue el proceso lsass.exe: se tienen que producir 9 retrocesos y escribir "lsm.exe", con lo que quedaría "lsm.exexe", pues el lsm.exe no ha sido capaz de sobreescribir el lsass.exe completo y el caracter especial de retroceso no borra, sólo mueve el cursor; así pues, hay que escribir dos espacios a continuación para borrar ese xe que sobra, quedando escrito "lsm.exe  ".
  • Estamos en el proceso mmc.exe y el anterior fue lsm.exe, pero en la realidad se escribió "lsm.exe  "; si sólo retrocedemos los 7 caracteres del lsm.exe y escribimos mmc.exe, el resultado será "lsmmc.exe", dado que el cursor estaba en el segundo espacio que habíamos añadido; así pues es necesario que sepamos que en la vuelta anterior agregamos dos espacios al final para que incrementemos en dos el número de retrocesos y que así se muestre "mmc.exe".

Como hemos dicho, el incremento es la diferencia que hay entre la longitud del nombre del proceso previo al anterior y la longitud del proceso anterior, pero sólo nos interesan los incrementos positivos (producidos cuanto la longitud del nombre previo al anterior es superior a la longitud del nombre anterior), de ahí el que pongamos el incremento como cero en el caso de que éste sea negativo. Una vez sabemos el incremento, podemos calcular los retrocesos que se han de dar, siendo una cadena de caracteres especiales de retroceso ("`b"), tantos como la suma de la longitud del nombre anterior más el incremento calculado antes; esta cadena de retrocesos la almacenamos en $Retroceso:

        $Incremento = $Previo.Length - $Anterior.Length
        If($Incremento -lt 0){$Incremento = 0}
        $Retroceso = "`b" * ($Anterior.Length + $Incremento)

Una vez calculados los retrocesos que se deben producir, debemos calcular cuántos espacios debemos poner después del nombre actual. Para ello, almacenamos como $Previo el nombre del proceso anterior y como $Anterior el nombre del proceso actual $_.Name. Volvemos a calcular el incremento y volvemos a ponerlo como cero en caso de ser negativo (esto es debido a que sólo tendremos que agregar espacios detrás del nombre del proceso actual en el caso de que tenga una menor longitud que el nombre del proceso anterior). Una vez calculado el incremento, almacenamos en $Borrado la cadena de espacios que se deben poner después del nombre:

        $Previo = $Anterior
        $Anterior = $_.Name
        $Incremento = $Previo.Length - $Anterior.Length
        If($Incremento -lt 0){$Incremento = 0}
        $Borrado = " " * $Incremento

Por último, sólo nos queda:

  • Escribir el nombre del proceso actual($Anterior), precedido de los retrocesos ($Retroceso) y seguido de los espacios ($Borrado).
  • Realizar la pausa de 100 milisegundos.
  • Cerrar el bucle ForEach.
  • Imprimir una línea en blanco para evitar que el inductor del sistema quede a continuación de lo último que se escribió en lugar de debajo.

Todo esto lo realizamos en estas líneas:

        Write-Host "$Retroceso$Anterior$Borrado" -NoNewline
        Start-Sleep -Milliseconds 100
    }
    ""

Este ejemplo está hecho con Get-Process, pero se podría hacer con otros Cmdlets que listen "algo", como por ejemplo Get-Services o Get-WmiObject (veremos un ejemplo que listará los programas instalados con Windows Installer, clase Win32_Product), e incluso con Cmdlets de otros PsSnapins distintos de los del Core de PowerShell, como por ejemplo Get-MailBox del PsSnapin Microsoft.Exchange.Management.PowerShell.Admin de Exchange. Vemos a continuación el código para cada uno de estos ejemplos, quedando señalado dónde se pondría el código para procesar el objeto de cada vuelta del bucle (se debería comentar/borrar la línea con el Start-Sleep):

Function Get-ServiceProgress
{
    Write-Host "Procesando el servicio " -NoNewline
    $Anterior = ""
    $Previo = ""
    Get-Service -WarningAction SilentlyContinue | ForEach{ `
        $Incremento = $Previo.Length - $Anterior.Length
        If($Incremento -lt 0){$Incremento = 0}
        $Retroceso = "`b" * ($Anterior.Length + $Incremento)
        $Previo = $Anterior
        $Anterior = $_.Name
        $Incremento = $Previo.Length - $Anterior.Length
        If($Incremento -lt 0){$Incremento = 0}
        $Borrado = " " * $Incremento
        Write-Host "$Retroceso$Anterior$Borrado" -NoNewline
        Start-Sleep -Milliseconds 100
        # Aquí se pone el código que procesa el servicio actual
    }
    ""
}

Function Get-ProductProgress
{
    Write-Host "Procesando el producto " -NoNewline
    $Anterior = ""
    $Previo = ""
    Get-WmiObject Win32_Product -WarningAction SilentlyContinue | ForEach{ `
        $Incremento = $Previo.Length - $Anterior.Length
        If($Incremento -lt 0){$Incremento = 0}
        $Retroceso = "`b" * ($Anterior.Length + $Incremento)
        $Previo = $Anterior
        $Anterior = $_.Name
        $Incremento = $Previo.Length - $Anterior.Length
        If($Incremento -lt 0){$Incremento = 0}
        $Borrado = " " * $Incremento
        Write-Host "$Retroceso$Anterior$Borrado" -NoNewline
        Start-Sleep -Milliseconds 100
        # Aquí se pone el código que procesa el producto actual
    }
    ""
}

Function Get-MailBoxProgress
{
    ""
    Write-Host "Procesando el buzón " -NoNewline
    $Anterior = ""
    $Previo = ""
    Get-Mailbox -ResultSize Unlimited `
                -WarningAction SilentlyContinue | ForEach{ `
        $Incremento = $Previo.Length - $Anterior.Length
        If($Incremento -lt 0){$Incremento = 0}
        $Retroceso = "`b" * ($Anterior.Length + $Incremento)
        $Previo = $Anterior
        $Anterior = $_.PrimarySmtpAddress
        $Incremento = $Previo.Length - $Anterior.Length
        If($Incremento -lt 0){$Incremento = 0}
        $Borrado = " " * $Incremento
        Write-Host "$Retroceso$Anterior$Borrado" -NoNewline
    }
    ""
}

Y esto fue todo :oP.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

 
A %d blogueros les gusta esto: