2010-02-05 24 views
7

Pisałem program linii poleceń, który będzie miał pasek stanu, podobnie jak wget.kasowanie wyjścia terminalowego na Linuksie

Głównym problemem, przed którym stoję, jest: jak usunąć to, co już wysłałem na stdout/stderr?

Wpadłem na pomysł: użyj znaku backspace "\ b" i skasuj dane wyjściowe, które wysłałem. Czy to najlepszy sposób? Czy to jedyny sposób? Czy istnieje lepszy sposób?

PS: Nie chcę używać niczego takiego jak ncurses. Zwykły stary C, proszę.

Dzięki


EDIT:

mogę też iść w górę i/lub w dół? Przykład: Mam 10 linii wyjściowych, chcę zmienić trzecią linię z Doing ABC na ABC: Done. Jak mogę to zrobić?

Czy ktoś może dodać więcej szczegółów na temat znaków VT102? Jakie są jego możliwości? Opublikuj dobre linki na ten temat, jeśli masz jakieś.

Dzięki

+0

Google 'vt100 codes' lub' vt102 codes'. –

+0

Zobacz odpowiedzi na to pytanie dotyczące czyszczenia danych wyjściowych programu terminalowego - http://stackoverflow.com/questions/1348563/clearing-output-of-a-terminal-program-linux-c-c – jschmier

Odpowiedz

3

Użyj "\ r", aby powrócić na początek wiersza i ewentualnie przepisać całą linię.

Poszukaj sekwencji sterujących VT102 - są to sekwencje znaków ESC ... sterujące terminalem.

+2

twoja odpowiedź całkowicie rozwiązuje moje pierwotne pytanie, chociaż mam pokusę, aby ncurses, teraz, gdy przeczytałem odpowiedź Johna .. – jrharshath

0

Jest to pasek postępu dla 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

dont want for bash. chcę dla c. bez basha. tylko C. do. – jrharshath

+6

Albo ... mógłbyś to przeczytać, a potem pomyśleć o tym, co czytasz. Może się zdarzyć, że powie Ci, jak to działa, i przy odrobinie więcej myśli, że będziesz w stanie wymyślić własną implementację c. Po prostu myśl. –

1

Nieznaczne wahania na własne rozwiązanie:

Można również wydrukować znak powrotu karetki (\r), co spowoduje powrót do początku linii.

2

Istnieje również możliwość korzystania z biblioteki Ncurses, która jest biblioteką dla interfejsu tekstowego, gdzie tego rodzaju zachowanie powinno być wspierane. Jednak może to być przesada dla czegoś takiego.

+0

Tak jak słusznie brzmi twoja odpowiedź, osoba pytająca zdecydowała, że ​​wolałaby jej nie używać. –

6

Podstawowe znaki kontrolne formatowania to backspace (\ b), tab (\ t), nowa linia (\ n) i powrót karetki (\ r). Jeśli potrzebujesz więcej, możesz użyć sekwencji pomocniczych ANSI X3.64/ISO/IEC 6429/ECMA-48; co najmniej the VT100 subset jest rozpoznawany przez większość nowoczesnych terminali i emulatorów. Zaletą korzystania z ncurses jest to, że będzie on sprawdzał możliwości twojego konkretnego terminala i będzie działał, nawet jeśli twój terminal używa innego zestawu sekwencji ucieczki.

+0

+1: Najlepsza odpowiedź; powinny zostać zaakceptowane. –

5

Należy pamiętać, że jeśli chodzi o zwykłe procedury standardowe, stdout jest po prostu strumieniem bajtów bez charakterystycznych cech wyświetlania; zależy to od urządzenia docelowego, które może być dowolne, od zwykłego terminalu w stylu VT100 do terminalu drukowanego do drukarki arkuszowej do plotera.

IMO, jesteś daleko lepiej przy użyciu biblioteki jak ncurses niż próbuje włamać razem swój własny kod zarządzania wyświetlacz z kodami ucieczki VT100, nawet dla stosunkowo prostego zadania takiego. Wiem, że chcesz trzymać się "zwykłego starego C", ale jest to zadanie, które wykracza poza granice zwykłego starego C.

+1

Skąd wiadomo, że tty wyjściowe rozumieją kody vt100? Zgadzam się z biblioteką curses (3) (lub wariantem). W przypadku czegoś takiego jest to dość proste – mpez0

0

O pasku postępu: coś takiego?

#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); 
}; 

Uwaga: unistd.h i usleep rzeczą jest po prostu fałszywy postęp operacji, postęp kod kreskowy sama tylko korzysta z biblioteki standardowej. Jego jedynymi założeniami dotyczącymi strumienia wyjściowego są: \ b faktyczne przejście do poprzedniej, pisanej postaci. Próbowałem go z powodzeniem w systemach Windows i Linux (z gnome-terminal), nie wiem, czy nie działa poprawnie z niektórymi emulatorami terminali. Przepraszam za nadmierną ilość komentarzy, napisałem to na inne forum, gdzie potrzebowałem wytłumaczyć praktycznie każdą linię kodu nowicjuszowi C.