2010-02-05 30 views
7

Estaba escribiendo un programa de línea de comandos que tendrá una barra de estado, muy similar a wget.borrando la salida del terminal en linux

El principal problema al que me enfrento es: ¿cómo elimino lo que ya he enviado a stdout/stderr?

Tenía idea: utiliza el carácter de retroceso '\ b' y borra la salida que he enviado. ¿Es esa la mejor manera? ¿Es la única manera? ¿Hay una mejor manera?

PD: No quiero usar nada como ncurses. Claro viejo C por favor.

Gracias


EDIT:

¿Puedo ir hacia arriba y/o hacia abajo? Ejemplo: Tengo 10 líneas de salida, quiero cambiar la 3ra línea de Doing ABC a ABC: Done. ¿Cómo puedo hacer eso?

Además, ¿alguien puede publicar más detalles sobre qué son los caracteres VT102? ¿Cuáles son sus capacidades? Por favor, publica buenos enlaces si tienes alguno.

Gracias

+0

Google 'vt100 codes' o' vt102 codes'. –

+0

Consulte las respuestas a esta pregunta sobre cómo borrar el resultado de un programa de terminal - http://stackoverflow.com/questions/1348563/clearing-output-of-a-terminal-program-linux-c-c – jschmier

Respuesta

3

Use '\ r' para volver al comienzo de la línea y posiblemente reescribir toda la línea.

Busque las secuencias de control VT102 - estas son secuencias de caracteres ESC ... para controlar su terminal.

+2

su respuesta aborda mi pregunta original por completo, aunque estoy tentado de ncurses, ahora que he leído la respuesta de John. – jrharshath

0

Es una barra de progreso para bash.

function gauge() 
{ 
     progress="$1" 
     total="$2" 
     width=`tput cols` 
     let gwidth=width-7 

     if [ "$total" == "0" ]; then 
       percent=100 
     else 
       set +e 
       let percent=progress*100/total; 
       set -e 
     fi 

     set +e 
     let fillcount=percent*gwidth/100 
     let nofillcount=gwidth-fillcount 
     set -e 

     fill=""; 
     if [ "$fillcount" -gt "0" ]; then 
       for i in `seq $fillcount`; do 
         fill="$fill""|" 
       done 
     fi; 
     nofill="" 
     if [ "$nofillcount" -gt "0" ]; then 
       for i in `seq $nofillcount`; do 
         nofill="$nofill"" "; 
       done 
     fi 
     echo -e -n "\r[""$fill""$nofill""] ""$percent""%"; 
} 
+1

No quiero para bash. quiero para c. sin bash. Sólo c. do. – jrharshath

+6

O ... podrías leerlo y luego pensar en lo que lees. Tal vez se dé cuenta de que le dice cómo funciona, y con un poco más de reflexión, podría idear su propia implementación de c. Solo un pensamiento. –

1

Una ligera variación en su propia solución:

También puede imprimir un retorno de carro (\r), que le vuelve al principio de la línea.

2

También existe la posibilidad de usar Ncurses, que es una biblioteca para la interfaz de usuario textual, donde este tipo de comportamiento debería tener algún soporte. Sin embargo, puede ser excesivo para algo como esto.

+0

Tan correcto como su respuesta es, el que pregunta ha decidido que preferiría no usarlo. –

6

Los caracteres básicos de control de formato son retroceso (\ b), tabulación (\ t), nueva línea (\ n) y retorno de carro (\ r). Si necesita más que eso, puede usar las secuencias de escape ANSI X3.64/ISO/IEC 6429/ECMA-48; al menos the VT100 subset es reconocido por la mayoría de los terminales y emuladores modernos. Una ventaja de usar ncurses es que buscará las capacidades de su terminal en particular y por lo tanto funcionará incluso si su terminal usa un conjunto diferente de secuencias de escape.

+0

+1: Mejor respuesta; debería haber sido aceptado. –

5

Debe recordar que, en lo que respecta a las rutinas estándar de stdio, stdout es solo un flujo de bytes sin características de visualización inherentes; eso depende del dispositivo de destino, que puede ser desde un terminal tipo VT100 regular a un terminal de copia impresa a una impresora alimentada por hojas a un trazador a lo que sea.

OMI, que está ahora mejor usar una biblioteca como ncurses que intenta cortar juntos su propio código de gestión de visualización con los códigos de escape VT100, incluso para una tarea relativamente simple como esto. Sé que quieres quedarte con la "vieja C simple", pero esta es una tarea que queda fuera de los límites de la antigua C.

+1

¿Cómo sabes que la salida tty comprende los códigos vt100? De acuerdo con la biblioteca de curses (3) (o variantes). Para algo como esto, es bastante simple – mpez0

0

Acerca de la barra de progreso: algo como esto?

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

typedef enum 
{ 
    false=0, 
    true=!false 
} bool; 

typedef struct 
{ 
    /* Start delimiter (e.g. [)*/ 
    char StartDelimiter; 
    /* End Delimiter (e.g. ])*/ 
    char EndDelimiter; 
    /* Central block (e.g. =)*/ 
    char Block; 
    /* Last block (e.g. >) */ 
    char CurBlock; 
    /* Width of the progress bar (in characters) */ 
    unsigned int Width; 
    /* Maximum value of the progress bar */ 
    double Max; 
    /* True if we have to print also the percentage of the operation */ 
    bool PrintPercentage; 
    /* True if the bar must be redrawn; 
     note that this must be just set to false before the first call, the function then will change it by itself. */ 
    bool Update; 
} ProgressBarSettings; 

/* Prints/updates the progress bar */ 
void PrintProgressBar(double Pos, ProgressBarSettings * Settings); 
/* Inits the settings of the progress bar to the default values */ 
void DefaultProgressBar(ProgressBarSettings * Settings); 

int main() 
{ 
    int i; 
    /* Init the bar settings */ 
    ProgressBarSettings pbs; 
    DefaultProgressBar(&pbs); 
    pbs.Max=200; 
    pbs.Width=60; 
    printf("Progress: "); 
    /* Show the empty bar */ 
    PrintProgressBar(0,&pbs); 
    for(i=0;i<=pbs.Max;i++) 
    { 
     /* Wait 50 msec */ 
     usleep(50000); 
     /* Update the progress bar */ 
     PrintProgressBar(i,&pbs); 
    } 
    puts(" Done"); 
    return 0; 
} 

/* Inits the settings of the progress bar to the default values */ 
void DefaultProgressBar(ProgressBarSettings * Settings) 
{ 
    Settings->StartDelimiter='['; 
    Settings->EndDelimiter=']'; 
    Settings->Block='='; 
    Settings->CurBlock='>'; 
    Settings->PrintPercentage=true; 
    Settings->Update=false; 
    Settings->Max=100; 
    Settings->Width=40; 
} 

/* Prints/updates the progress bar */ 
void PrintProgressBar(double Pos, ProgressBarSettings * Settings) 
{ 
    /* Blocks to print */ 
    unsigned int printBlocks=(unsigned int)(Settings->Width*Pos/Settings->Max); 
    /* Counter */ 
    unsigned int counter; 
    /* If we are updating an existing bar...*/ 
    if(Settings->Update) 
    { 
     /* ... we get back to its first character to rewrite it... */ 
     for(counter=Settings->Width+2+(Settings->PrintPercentage?5:0);counter;counter--) 
      putchar('\b'); 
    } 
    else 
     Settings->Update=true; /* next time we'll be updating it */ 
    /* Print the first delimiter */ 
    putchar(Settings->StartDelimiter); 
    /* Reset the counter */ 
    counter=Settings->Width; 
    /* Print all the blocks except the last; in the meantime, we decrement the counter, so in the end we'll have 
     the number of spaces to fill the bar */ 
    for(;printBlocks>1;printBlocks--,counter--) 
     putchar(Settings->Block); 
    /* Print the last block; if the operation ended, use the normal block, otherwise the one for the last block */ 
    putchar((Settings->Max==Pos)?Settings->Block:Settings->CurBlock); 
    /* Another block was printed, decrement the counter */ 
    counter--; 
    /* Fill the rest of the bar with spaces */ 
    for(;counter;counter--) 
     putchar(' '); 
    /* Print the end delimiter */ 
    putchar(Settings->EndDelimiter); 
    /* If asked, print also the percentage */ 
    if(Settings->PrintPercentage) 
     printf(" %3d%%",(int)(100*Pos/Settings->Max)); 
    /* Flush the output buffer */ 
    fflush(stdout); 
}; 

Nota: el unistd.h y usleep cosa es falsa sólo para el progreso de una operación, el código de barra de progreso en sí sólo utiliza la biblioteca estándar. Sus únicas suposiciones sobre el flujo de salida son que \ b en realidad llega al carácter escrito anterior. Lo intenté con éxito en Windows y Linux (con gnome-terminal), no sé si no funciona correctamente con algunos emuladores de terminal. Lo siento por la cantidad excesiva de comentarios, lo escribí para otro foro donde necesitaba explicar prácticamente cada línea del código a un C novato.

Cuestiones relacionadas