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

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

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

25%[=============>______________________________________] 25,000 100.0K/s (los guiones bajos son espacios; simplemente no pude entender cómo incluir más de un espacio consecutivo allí)

Sin embargo, en lugar de escribir otra línea en la salida estándar y agregar otra barra de progreso, la actualiza, así:

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

Y wgettampoco es el único ejemplo de ello. Por ejemplo, cuando canaliza algo lessy luego sale, el mensaje original sigue ahí, 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? ¿Solo funciona para una sola línea a la vez? ¿Puedo usarlo en C?

Respuesta1

En primer lugar, tu pregunta no tiene nada que ver con bash sino con la terminal. El terminal responde para mostrar el texto de los programas y bash no tiene control sobre los programas una vez que se inician.

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, consulte http://www.termsys.demon.co.uk/vtansi.htmPuedes por ejemplo

  • posicionar el cursor al principio 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 del terminal, por ejemplo, funcionan sólo con Eterm o xterm.maldiciones- es una biblioteca de programación que permite crear programas interactivos con el terminal para que no tengas que utilizar secuencias de escape.

Cómo sobrescribir una línea existente con secuencias de 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 de terminales

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

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

El \rretorno de carro o colocará el cursor al principio 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 característica más avanzada del terminal, 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 guardado de líneas desplazadas desde la parte superior de la ventana está deshabilitado hasta que se restablezca la pantalla normal. La entrada term‐cap(5) para xterm permite al editor visual vi(1) cambiar a la pantalla alternativa para editar y restaurar la pantalla al salir. Una entrada de menú emergente simplifica el cambio entre las pantallas normal y alternativa para cortar y pegar.

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

Respuesta2

En lugar de usar echowhich 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 ""

Respuesta3

También recomendaría a aquellos que encontraron este tema que echen un vistazo aCÓMO de solicitud de Bash: movimiento del cursor.

Ejemplos:

- Position the Cursor:
  \033[<L>;<C>H
     Or
  \033[<L>;<C>f
  puts the cursor at line L and column C.
- Move the cursor up N lines:
  \033[<N>A
- Move the cursor down N lines:
  \033[<N>B
- Move the cursor forward N columns:
  \033[<N>C
- Move the cursor backward N columns:
  \033[<N>D

- Clear the screen, move to (0,0):
  \033[2J
- Erase to end of line:
  \033[K

- Save cursor position:
  \033[s
- Restore cursor position:
  \033[u

Algunos ejemplos en C:

void saveCursorPosition() {
  printf("\033[s");
}

void restoreCursorPosition() {
  printf("\033[u");
}

void lineUP(short int times) {
  printf("\033[%iA", times);
}

void lineDown(short int times) {
  printf("\033[%iB", times);
}

Muestra de programa:

#include <stdio.h>
#include <unistd.h>

void saveCursorPosition() {
  printf("\033[s");
}

void restoreCursorPosition() {
  printf("\033[u");
}

void lineUP(short int times) {
  printf("\033[%iA", times);
}

void moveCursorBackwards(short int times) {
  printf("\033[%iD", times);
}

void printMainText() {

  printf("\n ╔═══════════════════════════════╗");
  printf("\n ║                               ║");
  printf("\n ║ Progress Bar                  ║");
  printf("\n ║                               ║");
  printf("\n ║ []                            ║");
  printf("\n ║                               ║");
  printf("\n ║ Press Ctrl+C to close         ║");
  printf("\n ║                               ║");
  printf("\n ╚═══════════════════════════════╝\n");

}

int main(int argc, char **argv) {

  printMainText();

  for (int progress=0; progress <= 10; progress++) {

    saveCursorPosition();
    lineUP(5);

    printf("\r ║     [");
    fflush(stdout);

    for (int i=0; i<progress; i++) {
      printf("=>]");
      fflush(stdout);
      moveCursorBackwards(2);
    }

    moveCursorBackwards(progress + 5);

    printf("%i%%", progress * 10);
    fflush(stdout);

    restoreCursorPosition();

    sleep(1);

  }

  return 0;

}

información relacionada