¿Cómo cambiar el contenido de una línea en la terminal en lugar de escribir una nueva?

24

Entonces, cuando wgetobtiene una página web, le muestra una barra de estado que indica cuánto se descargan los archivos. Se parece a esto:

25%[=============>______________________________________] 25,000 100.0K/s (los guiones bajos son espacios; simplemente no pude encontrar la manera de obtener más de un espacio consecutivo allí)

Sin embargo, en lugar de escribir otra línea en stdout y agregar otra barra de progreso, la actualiza, así:

50%[===========================>________________________] 50,000 100.0K/s

Y wgettampoco es el único ejemplo de esto. Por ejemplo, cuando canaliza algo lessy luego sale, su solicitud original todavía está allí, junto con el resultado de los comandos que ejecutó anteriormente. Es como si nunca te hubieras ido.

Entonces, mis preguntas son, ¿cómo se llama esto? ¿Cómo lo implemento? ¿Funciona solo para una sola línea a la vez? ¿Puedo usar esto en C?

fouric
fuente
55
Recomiendo leer BashFAQ 44 . Puede que le resulte interesante.
jw013

Respuestas:

32

En primer lugar, su pregunta no tiene nada que ver con bash sino con el terminal. El terminal está respondiendo para mostrar el texto de los programas y bash no tiene control sobre los programas una vez que se iniciaron.

Los terminales ofrecen secuencias de control para controlar el color, la fuente, la posición del cursor y más. Para obtener una lista de secuencias de terminales estandarizadas, visite http://www.termsys.demon.co.uk/vtansi.htm . Puede, por ejemplo,

  • Posicione el cursor al comienzo de la línea
  • borrar la línea después
  • escribe una nueva línea

para crear una barra de progreso.

Las secuencias de escape de terminal más avanzadas suelen depender de la terminal, por ejemplo, funcionan solo con Eterm o xterm. ncurses : es una biblioteca de programación que crea programas interactivos con el terminal para que no tenga que usar secuencias de escape.

Cómo sobrescribir una línea existente con secuencias terminales

echo long text
sleep 1
printf "\033[1A"  # move cursor one line up
printf "\033[K"   # delete till end of line
echo foo

Cómo sobrescribir una línea existente sin secuencia terminal

Una solución simple es no escribir una nueva línea al final, sino escribir el retorno de carro, que básicamente restablece el cursor al comienzo de la línea, por ejemplo:

echo -n first 
sleep 1 
echo -ne "\rsecond"
echo

El \rretorno de carro o colocará el cursor al comienzo de la línea y le permitirá sobrescribir el contenido de la línea.

Cambiar entre buffers como lessovi

El comportamiento de lesstambién se debe a una función de terminal más avanzada, la pantalla alternativa:

En el modo VT102, hay secuencias de escape para activar y desactivar un búfer de pantalla alternativo, que tiene el mismo tamaño que el área de visualización de la ventana. Cuando se activa, la pantalla actual se guarda y se reemplaza con la pantalla alternativa. El almacenamiento de las líneas desplazadas desde la parte superior de la ventana se desactiva hasta que se restaura la pantalla normal. La entrada term-cap (5) para xterm permite que el editor visual vi (1) cambie a la pantalla alternativa para editar y restaurar la pantalla al salir. Una entrada de menú emergente facilita el cambio entre las pantallas normales y alternativas para cortar y pegar.

http://rosettacode.org/wiki/Terminal_control/Preserve_screen enumera algunos ejemplos de cómo hacerlo usted mismo, ya sea a través de tput o mediante algunas secuencias de escape.

Ulrich Dangel
fuente
15

En lugar de usar echoque agrega automáticamente una nueva línea a la cadena, use printf "%s\r" whatever: el retorno de carro envía el cursor al comienzo de la línea actual. ejemplo:

seq 1 15 | while read num; do printf "%2d\r" $num; sleep 1; done; echo ""
Glenn Jackman
fuente
dependiendo del cursor de su terminal, puede ser más agradable de hacerprintf "\r%2d " $num
glenn jackman