在Linux上清除终端输出

7

我正在编写一个命令行程序,它将像wget一样拥有一个状态栏。

我面临的主要问题是:如何删除我已经发送到stdout/stderr的内容?

我有一个想法:使用退格符'\b'和擦除我已发送的输出。这是最好的方法吗?它是唯一的方法吗?是否有更好的方法?

PS:我不想使用类似ncurses的东西。请用简单的旧C语言。

谢谢


编辑:

我能否向上或向下移动?例如:我有10行输出,我想将第3行从“Doing ABC”更改为“ABC: Done”。我该怎么做?

此外,有人可以发布有关VT102字符的详细信息吗?它的功能是什么?如果您有好的链接,请发布。

谢谢


请搜索谷歌上的 vt100代码vt102代码 - Eric Seppanen
请参阅有关清除终端程序输出的此问题的响应 - https://dev59.com/4nM_5IYBdhLWcg3wgzZl - jschmier
7个回答

6
基本格式控制字符包括退格符(\b)、制表符(\t)、换行符(\n)和回车符(\r)。如果您需要更多的控制字符,则可以使用ANSI X3.64 / ISO/IEC 6429 / ECMA-48 转义序列;至少VT100子集被大多数现代终端和模拟器所识别。使用ncurses的优点是它会查找您特定终端的功能,因此即使您的终端使用不同的转义序列,它也能正常工作。

5
请记住,就常规的stdio例程而言,stdout只是一个没有固有显示特性的字节流;这取决于目标设备,可以是从普通的VT100风格终端到硬拷贝终端、纸张送进式打印机、绘图仪等任何设备。
在我看来,使用像ncurses这样的库比尝试用VT100转义码拼凑自己的显示管理代码要好得多,即使是像这样相对简单的任务。我知道你想坚持使用“纯老式C”,但这个任务超出了纯老式C的范畴。

1
你如何知道输出tty理解vt100代码?同意使用curses(3)(或变体)库。对于这样的事情,它非常简单。 - mpez0

3

使用'\r'回到行首并可能重写整行。

寻找VT102控制序列-这些是字符序列ESC ...,用于控制您的终端。


2
你的回答完全解决了我的问题,尽管我看到John的回答后也对ncurses产生了兴趣。 - jrharshath

2

还有一种可能是使用Ncurses,它是一个用于文本界面用户界面的库,这种行为应该有一些支持。然而,对于像这样的东西来说,使用它可能会过度。


尽管你的答案是正确的,但提问者已决定不使用它。 - Ignacio Vazquez-Abrams

1

稍作修改你的解决方案:

您还可以打印回车符 (\r), 这样将会回到行首。


0
这是一个适用于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
只需要C语言,不要Bash。只用C语言,C语言。 - jrharshath
6
你可能会意识到它告诉你它是如何工作的,再多思考一下,你就可以自己写出一个C实现。只是一个想法。 - James Caccese

0
关于进度条:类似这样的吗?
#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-terminal) 上成功尝试过,但不知道它是否能在一些终端仿真器上正确工作。 对于评论的过多,很抱歉,我写这段代码时是为了另一个论坛,在那里我需要向 C 初学者解释每行代码的实际含义。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接