стирание вывода терминала в linux

Я писал программу командной строки, которая будет иметь строку состояния, очень похожую на wget.

Основная проблема, с которой я столкнулся: как удалить то, что я уже отправил в stdout/stderr?

У меня была идея: использовать символ возврата '\ b' и стереть вывод, который я отправил. Это лучший способ? Это единственный способ? Есть ли способ лучше?

PS: я не хочу использовать что-то вроде ncurses. Обычный старый C, пожалуйста.

Спасибо


РЕДАКТИРОВАТЬ:

Могу ли я также идти вверх и/или вниз? Пример: у меня есть 10 строк вывода, я хочу изменить 3-ю строку с Doing ABC на ABC: Done. Как я могу это сделать?

Кроме того, кто-нибудь может опубликовать более подробную информацию о том, что такое символы VT102? Каковы его возможности? Пожалуйста, разместите хорошие ссылки на это, если у вас есть.

Спасибо


person jrharshath    schedule 05.02.2010    source источник
comment
Google vt100 codes или vt102 codes.   -  person Eric Seppanen    schedule 05.02.2010
comment
См. ответы на этот вопрос об очистке вывода терминальной программы - stackoverflow.com/questions/1348563/   -  person jschmier    schedule 05.02.2010


Ответы (7)


Используйте '\r', чтобы вернуться к началу строки и, возможно, переписать всю строку.

Ищите управляющие последовательности VT102 — это последовательности символов ESC… для управления вашим терминалом.

person user231967    schedule 05.02.2010
comment
ваш ответ полностью отвечает на мой первоначальный вопрос, хотя теперь, когда я прочитал ответ Джона, у меня возникает соблазн ругаться. - person jrharshath; 06.02.2010

Основными управляющими символами форматирования являются символы возврата (\b), табуляции (\t), новой строки (\n) и возврата каретки (\r). Если вам нужно больше, вы можете использовать ANSI X3.64 / ISO/IEC 6429 / < escape-последовательности href="http://www.ecma-international.org/publications/standards/Ecma-048.htm" rel="noreferrer">ECMA-48; как минимум подмножество VT100 распознается большинством современных терминалов и эмуляторов . Преимущество использования ncurses заключается в том, что он будет искать возможности вашего конкретного терминала, поэтому он будет работать, даже если ваш терминал использует другой набор escape-последовательностей.

person mark4o    schedule 05.02.2010

Вы должны помнить, что в том, что касается обычных подпрограмм stdio, stdout — это просто поток байтов без встроенных характеристик отображения; это зависит от целевого устройства, которым может быть что угодно, от обычного терминала в стиле VT100 до терминала для печати, от листового принтера до плоттера и до чего угодно.

IMO, вам намного лучше использовать такую ​​библиотеку, как ncurses, чем пытаться собрать собственный код управления дисплеем с escape-кодами VT100, даже для такой относительно простой задачи, как эта. Я знаю, что вы хотите придерживаться «старого доброго C», но эта задача выходит за рамки старого доброго C.

person John Bode    schedule 05.02.2010
comment
Откуда вы знаете, что выходной tty понимает коды vt100? Согласен с библиотекой curses(3) (или вариант). Для чего-то подобного это довольно просто - person mpez0; 06.02.2010

Существует также возможность использования Ncurses, библиотеки для текстового пользовательского интерфейса, где этот тип поведение должно иметь некоторую поддержку. Тем не менее, это может быть излишним для чего-то вроде этого.

person Tobias Wärre    schedule 05.02.2010
comment
Каким бы правильным ни был ваш ответ, спрашивающий решил, что лучше его не использовать. - person Ignacio Vazquez-Abrams; 05.02.2010

Небольшое изменение вашего собственного решения:

Вы также можете напечатать возврат каретки (\r), который вернет вас в начало строки.

person Stéphan Kochen    schedule 05.02.2010

Это индикатор выполнения для 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""%";
}
person Notinlist    schedule 05.02.2010
comment
не хочу для Баша. хочу для c. нет баш. только с. в. - person jrharshath; 05.02.2010
comment
Или... вы могли бы прочитать это, а потом подумать о прочитанном. Возможно, вы поймете, что он говорит вам, как это работает, и, немного подумав, вы сможете придумать свою собственную реализацию на c. Просто мысль. - person James Caccese; 05.02.2010

Про индикатор выполнения: что-то вроде этого?

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

Примечание: unistd.h и usleep просто имитируют ход операции, сам код индикатора выполнения просто использует стандартную библиотеку. Его единственное предположение о выходном потоке состоит в том, что \b фактически получает предыдущий записанный символ. Я успешно попробовал его в Windows и Linux (с gnome-терминалом), не знаю, работает ли он правильно с некоторыми эмуляторами терминала. Извините за чрезмерное количество комментариев, я написал это для другого форума, где мне нужно было объяснить новичку C практически каждую строку кода.

person Matteo Italia    schedule 05.02.2010