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 Una Cuenta Hacia Delante o Hacia Detrás

Posted by urpiano en Domingo 2 \02\UTC diciembre \02\UTC 2012

En ocasiones podemos querer que se vaya mostrando un cuenta hacia adelante o hacia detrás en algún script que tengamos. La forma "bonita" de hacer esto es que se vea un texto fijo y un número a su lado que vemos cómo se va incrementando o cómo va decreciendo.

El secreto para poder hacer esto reside en dos cosas:

  • Caracter especial de retroceso, que nos permite echar un carácter hacia atras en la pantalla (hay que tener en cuenta que lo que hace es mover el cursor a donde se va a escribir, no borra lo que haya escrito) de manera que si se escribe después un caracter éste sustituirá al que había. Este caracter especial es `b (los caracteres especiales van precedidos del símbolo de escape de PowerShell, el acento grave). Más sobre caracteres especiales en about_Escape_Characters y about_Special_Characters.
  • Uso del modificador -NoNewLine del Cmdlet Write-Host: de forma predeterminada, Write-Host escribe el texto que se le indique y termina haciendo un salto de línea; el uso de este modificador hace que ese salto de línea no se produzca, lo que es ideal para nuestros propósitos.

Puestos en conjunción estos dos elementos, podemos escribir la leyenda de la cuenta adelante sin hacer salto de línea, para que luego en un bucle se vaya escribiendo el número en progreso, usando el caracter de retorno para poder sobreescribir el número de la anterior vuelta

Veamos un ejemplo de ésto para hacer una cuenta hacia adelante. Se trata de la función Get-CountUp (copiamos y pegamos en una consola de PowerShell y después la invocamos:

Function Get-CountUp
{
    ""
    Write-Host "Elemento Nº  " -NoNewLine
    1..1100 | ForEach{ `
        Start-Sleep -Milliseconds 5
        $Retroceso = "`b" * ("{0:N0}" -f ($_ - 1)).Length
        Write-Host "$Retroceso$("{0:N0}" -f $_)" -NoNewLine
    }
    ""
}

Vamos a explicar qué es lo que hace.

En primer lugar, escribimos una línea en pantalla, para separarnos de lo que hay, y a continuación ponemos la leyenda de la cuenta adelante, en nuestro ejemplo Elemento Nº y utilizamos el modificador -NoNewLine para evitar que lo siguiente que escribamos se haga en otra línea; observemos que después del no hay un sólo espacio, si no dos (explicaré porqué más adelante):

""
Write-Host "Elemento Nº  " -NoNewLine

A continuación mandamos a un bucle ForEach, por encaminamiento, un array de números, del 1 al 1.100. Estos números serán los que iremos mostrando en la cuanta hacia adelante, gracias a ir tratándolos en el bucle ForEach. Además de ésto, ya dentro del bucle estableceremos una pausa de 5 milisegundos, para que no sea todo tan rápido:

1..1100 | ForEach{ `
    Start-Sleep -Milliseconds 5

Vamos ahora con el meollo. Primero calculamos cuántos retrocesos debemos dar, lo que va en función de la longitud de la cadena que representaba el número de la anterior vuelta. Hay que tener en cuenta dos cosas:

  • El formato que tenga el número: si no tiene formato, basta con hacer ($_ – 1).ToString().Length para saber el número de caracteres que tenía el número de la anterior vuelta. Si tiene formato, como en el ejemplo, hay que calcular la longitud teniendo en cuenta dicho formato; como estamos usando un formato numérico sin decimales (“{0:N0}”), deberemos mirar la longitud del número anterior con el formato numérico, que es lo que se hace en el ejemplo, para asignar valor a la variable $Retroceso; esto es así, pues al llegar a los cuatro dígitos, por ejemplo, serán necesarios 5 retrocesos, al haber añadido el formato el punto separador de miles.
  • El comienzo del proceso. En este momento el numero anterior al primero, al calcularlo, es cero, lo que es una longitud de uno, que provocará un retroceso de 1 caracter, de ahí el que en la leyenda pusiéramos dos espacios después del , para que así se conserve uno, que separe el número que se muestra del .

Una vez calculado el retroceso, usamos Write-Host con el modificador -NoNewLine y mostramos los retrocesos calculados seguidos del número actual con formato:

    $Retroceso = "`b" * ("{0:N0}" -f ($_ - 1)).Length
    Write-Host "$Retroceso$("{0:N0}" -f $_)" -NoNewLine

Por último, cerramos el bucle y, muy importante, escribimos una línea en blanco; de esta manera evitaremos que el inductor de PowerShell aparezca a continuación del número, y no una línea por debajo:

}
""

Chulo y fácil ¿verdad?

Si lo que queremos es realizar una cuenta hacia detrás deberemos tener en cuenta que no sólo hay que realizar tantos retrocesos como longitud tenía el número de la anterior vuelta, si no que, como puede tener más longitud el anterior número que el actual y el caracter especial de retroceso no borra, habrá que escribir después del número tantos espacios como dígitos sobran del número anterior. Esto obliga también a mirar si el número previo al anterior tenía más longitud que el anterior, para añadir tantos retrocesos como espacios se incluyeron. Veamos un ejemplo:

  • Estamos en el número 999 y el anterior fue el 1.000: se tienen que producir 5 retrocesos y escribir el número 999, con lo que quedaría "99900", pues el 999 no ha sido capaz de sobreescribir el 1.000 completo y, recordemos, el caracter especial de retroceso no borra, sólo mueve el cursor; así pues, hay que escribir dos espacios a continuación para borrar esos dos ceros que sobran, quedando escrito "999  ".
  • Estamos en el número 998 y el anterior fue el 999, pero en la realidad se escribió "999  "; si sólo retrocedemos los tres dígitos del 999 y escribimos el 998, el resultado será "99998", 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 el 998.

Veamos pues un ejemplo de ésto para hacer una cuenta hacia detrás. Se trata de la función Get-CountDown (copiamos y pegamos en una consola de PowerShell y después la invocamos:

Function Get-CountDown
{
    $Longitud1 = 0
    $Longitud2 = 0
    ""
    Write-Host "Elemento Nº      " -NoNewLine
    1100..1| ForEach{ `
        Start-Sleep -Milliseconds 5
        $Incremento = $Longitud1 - $Longitud2
        $Longitud1 = ("{0:N0}" -f ($_ + 1)).Length
        $Numero = "{0:N0}" -f $_
        $Longitud2 = $Numero.Length
        $Borrado = " " * ($Longitud1 - $Longitud2)
        $Retroceso = "`b" * ($Longitud1 + $Incremento)
        Write-Host "$Retroceso$Numero$Borrado" -NoNewLine
    }
    ""
}

Las primeras líneas de este código se encargan de:

  • Iniciar las variables que se utilizarán para almacenar el número de caracteres del número actual ($Longitud2) y del anterior ($Longitud1).
  • Escribimos una línea en pantalla, para separarnos de lo que hay, y a continuación ponemos la leyenda de la cuenta atrás, en nuestro ejemplo Elemento Nº y utilizamos el modificador -NoNewLine para evitar que lo siguiente que escribamos se haga en otra línea; observemos que después del no hay un sólo espacio, si no seis por idéntica razón a la que vimos que había dos en el ejemplo de la cuenta hacia delante.
  • Mandamos a un bucle ForEach por encaminamiento un array de números, del 1.100 al 1. Estos números serán los que iremos mostrando en la cuenta hacia detrás, gracias a ir tratándolos en el bucle ForEach. Además de ésto, ya dentro del bucle, estableceremos una pausa de 5 milisegundos, para que no sea todo tan rápido.

$Longitud1 = 0
$Longitud2 = 0
""
Write-Host "Elemento Nº      " -NoNewLine
1100..1| ForEach{ `
    Start-Sleep -Milliseconds 5

Ahora vamos a calcular el incremento que hay que añadir a los retrocesos, por si en la vuelta anterior hubo que añadir espacios al final del número. Este incremento es el resultado de restar la longitud del número anterior a la del número previo a éste (si estamos en la vuelta del bucle que procesa el 998 tendremos que $Incremento = 5 – 3, por ser “1.000”.Length – “999”.Length; si estamos en la que procesa el 997 tendremos que $Incremento = 3 – 3, por ser “999”.Length – “998”.Length).

    $Incremento = $Longitud1 - $Longitud2

Una vez calculado el incremento que hay que sumar a los retrocesos, recopilamos los datos de la vuelta actual, es decir, la longitud del número anterior como cadena con formato numérico ($Longitud1), el número actual como cadena con formato numérico ($Numero) y la longitud de esta cadena ($Longitud2):

    $Longitud1 = ("{0:N0}" -f ($_ + 1)).Length
    $Numero = "{0:N0}" -f $_
    $Longitud2 = $Numero.Length

Ahora obtenemos el número de espacios a escribir después del número, para borrar posibles dígitos que sobran, que son tantos como la diferencia de longitudes del número anterior y el actual ($Borrado), como cadenas con formato numérico. Obtenemos también los retrocesos, que se deben escribir antes que el número, que son tantos como la suma de la longitud del número anterior, como cadena con formato numérico, más el incremento que calculamos anteriormente. Una vez calculados, escribimos el número precedido de los retornos y continuado por los espacios, añadiendo el modificador -NoNewLine para que no se produzca un salto de línea después de la escritura:

    $Borrado = " " * ($Longitud1 - $Longitud2)
    $Retroceso = "`b" * ($Longitud1 + $Incremento)
    Write-Host "$Retroceso$Numero$Borrado" -NoNewLine

Por último, cerramos el bucle y, muy importante, escribimos una línea en blanco; de esta manera evitaremos que el inductor de PowerShell aparezca a continuación del número, y no una línea por debajo:

}
""

¡¡¡Menudo ladrillo me he marcado para una cosa tan tonta!!! :oP

Una respuesta to “PowerShell Tip: Mostrar Una Cuenta Hacia Delante o Hacia Detrás”

  1. […] PowerShell Tip: Mostrar Una Cuenta Hacia Delante o Hacia Detrás […]

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: