Entonces, cuando wget
accede 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 wget
tampoco es el único ejemplo de ello. Por ejemplo, cuando canaliza algo less
y 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 \r
retorno 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 less
ovi
El comportamiento de less
tambié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 echo
which 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;
}