2009-03-18 20 views
30

En la mayoría de las cubiertas modernas, puede presionar las flechas hacia arriba y hacia abajo y, cuando se solicite, colocará las órdenes anteriores que haya ejecutado. Mi pregunta es, ¿cómo funciona esto?Cómo sobrescribir la salida estándar en C

Me parece que el shell está de alguna manera manipulando stdout para sobrescribir lo que ya ha escrito.

Me di cuenta de que programas como wget hacen esto también. ¿Alguien tiene alguna idea de cómo lo hacen?

Respuesta

40

No está manipulando la salida estándar - está sobrescribiendo los caracteres que ya se han mostrado en la terminal.

Prueba esto:

#include <stdio.h> 
#include <unistd.h> 
static char bar[] = "=======================================" 
        "======================================>"; 
int main() { 
    int i; 
    for (i = 77; i >= 0; i--) { 
     printf("[%s]\r", &bar[i]); 
     fflush(stdout); 
     sleep(1); 
    } 
    printf("\n"); 
    return 0; 
} 

Eso está bastante cerca de wget 's de salida, ¿verdad? \r es un retorno de carro, que el terminal interpreta como "mover el cursor de regreso al inicio de la línea actual".

su concha, si es bash, utiliza el GNU Readline library, que proporciona mucha funcionalidad más general, incluyendo la detección de tipos de terminales, gestión de la historia, las asociaciones de teclas programables, etc.

Una cosa más - en caso de duda, la fuente para su wget, su caparazón, etc. están todos disponibles.

2

Se hace con la biblioteca readline ... No estoy seguro de cómo funciona detrás de las escenas, pero no creo que tenga nada que ver con stdout o transmisiones. Sospecho que readline utiliza algún tipo de comandos de terminal crípticos (para mí, al menos), es decir, coopera con el programa de terminal que realmente muestra su sesión de shell. No sé si puede obtener un comportamiento de línea de lectura simplemente imprimiendo el resultado.

(Piense en esto: la salida estándar puede ser redirigido a un archivo, pero las teclas de arriba/abajo de flecha truco no funciona en los ficheros.)

21

Para sobrescribir la línea de salida estándar actual (o partes de ella) utilice \r (o \b). El carácter especial \r (retorno de carro) devolverá el símbolo de intercalación al principio de la línea, lo que le permitirá sobrescribirlo. El carácter especial \b traerá el cursor hacia atrás una sola posición, lo que le permite sobrescribir el último carácter, p.

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

int i; 
const char progress[] = "|/-\\"; 

for (i = 0; i < 100; i += 10) { 
    printf("Processing: %3d%%\r",i); /* \r returns the caret to the line start */ 
    fflush(stdout); 
    sleep(1); 
} 
printf("\n"); /* goes to the next line */ 
fflush(stdout); 

printf("Processing: "); 
for (i = 0; i < 100; i += 10) { 
    printf("%c\b", progress[(i/10)%sizeof(progress)]); /* \b goes one back */ 
    fflush(stdout); 
    sleep(1); 
} 
printf("\n"); /* goes to the next line */ 
fflush(stdout); 

Uso fflush(stdout); porque standard output is usually buffered y la información no puede ser de otra manera imprime inmediatamente en la salida o terminal

+1

Es posible que desee agregar un #include para la función de suspensión. – Prairiedogg

0

Usted puede utilizar retorno de carro para simular esto.

#include <stdio.h> 

int main(int argc, char* argv[]) 
{ 
    while(1) 
    { 
     printf("***********"); 
     fflush(stdout); 
     sleep(1); 
     printf("\r"); 
     printf("..........."); 
     sleep(1); 
    } 

    return 0; 
} 
11

Además de \ r y \ b, eche un vistazo a ncurses para un control avanzado sobre lo que está en la pantalla de la consola. (Incluyendo columnas, moviéndose arbitrariamente, etc.).

0

La manera más simple es imprimir para excluir el carácter de retorno de carro ('\ r').

El cursor se moverá al inicio de la línea, lo que le permite sobrescribir sus contenidos.

1

El programa hace esto imprimiendo caracteres especiales que el terminal interpreta de una manera especial.La versión más simple de esto es (en la mayoría de los terminales linux/unix) para imprimir '\ r' (retorno de carro) a la salida normal que restablece la posición del cursor al primer carácter en la línea actual. Entonces, lo que escriba a continuación sobrescribirá la línea que escribió anteriormente. Esto se puede usar para indicadores de progreso simples, por ejemplo.

int i = 0; 
while (something) { 
    i++; 
    printf("\rprocessing line %i...", i); 
    ... 
} 

Pero existen secuencias de caracteres de escape más complicadas que se interpretan de varias maneras. Se pueden hacer todo tipo de cosas con esto, como colocar el cursor en una posición específica en la pantalla o configurar el color del texto. Si o cómo se interpretan estas secuencias de caracteres depende de su terminal, pero una clase común admitida por la mayoría de los terminales es ansi escape sequences. Así que si desea que el texto en rojo, pruebe:

printf("Text in \033[1;31mred\033[0m\n"); 
+0

No es el shell el que procesa estos caracteres especiales, es el terminal en el que se ejecuta el shell, es decir, una instancia de xterm, rxvt, konsole o similar. – sleske

+0

usted por supuesto tiene razón. – sth

5

Un programa que se ejecuta en un terminal de texto/consola puede manipular el texto que se muestra en su consola de diversas maneras (escribir en negrita, mover el cursor, la pantalla clara, etc.). Esto se logra imprimiendo secuencias de caracteres especiales, llamadas "escape sequences" (porque generalmente comienzan con Escape, ASCII 27).

Si stdout va a un terminal que comprende estas secuencias de escape, la pantalla del terminal cambiará en consecuencia.

Si redirige la salida estándar a un archivo, las secuencias de escape aparecerán en el archivo (que por lo general no es lo que quiere).

no existe un estándar completo para secuencias de escape, pero la mayoría de los terminales utilizan las secuencias introducidas por VT100, con muchas extensiones. Esto es lo que entienden la mayoría de los terminales en Unix/Linux (xterm, rxvt, konsole) y otros como PuTTY.

En la práctica, no codificaría directamente secuencias de escape en su software (aunque podría), pero use una biblioteca para imprimirlas, como ncurses o GNU readline mencionadas anteriormente. Esto permite la compatibilidad con diferentes tipos de terminales.

Cuestiones relacionadas