简体   繁体   English

擦除linux上的终端输出

[英]erasing terminal output on linux

I was writing a command line program which will have a status bar, much like wget. 我正在编写一个命令行程序,它将有一个状态栏,就像wget一样。

The main problem I'm facing is: how do I delete what I've already sent into stdout/stderr? 我面临的主要问题是:如何删除我已经发送到stdout / stderr的内容?

I had on idea: use the backspace char '\\b' and erase the output I've sent. 我有想法:使用退格字符'\\ b'并删除我发送的输出。 Is that the best way? 这是最好的方式吗? Is it the only way? 这是唯一的方法吗? Is there a better way? 有没有更好的办法?

PS: I don't want to use anything like ncurses. PS:我不想使用像ncurses这样的东西。 Plain old C please. 平原老C请。

Thanks 谢谢


EDIT: 编辑:

Can I also go up and/or down? 我也可以往上走/走吗? Example: I have 10 lines of output, I want to change the 3rd line from Doing ABC to ABC: Done . 示例:我有10行输出,我想将第3行从Doing ABC更改为ABC: Done How can I do that? 我怎样才能做到这一点?

Also, can anyone post more details about what VT102 characters are? 此外,任何人都可以发布有关VT102字符的更多详细信息吗? What are its capabilities? 它的功能是什么? Please post good links on this if you have any. 如果您有任何好的链接,请在此发布。

Thanks 谢谢

The basic formatting control characters are backspace (\\b), tab (\\t), newline (\\n), and carriage return (\\r). 基本格式控制字符是退格(\\ b),制表符(\\ t),换行符(\\ n)和回车符(\\ r \\ n)。 If you need more than that then you can use ANSI X3.64 / ISO/IEC 6429 / ECMA-48 escape sequences; 如果您需要更多,那么您可以使用ANSI X3.64 / ISO / IEC 6429 / ECMA-48转义序列; at least the VT100 subset is recognized by most modern terminals and emulators. 至少VT100子集被大多数现代终端和仿真器识别。 An advantage of using ncurses is that it will look up the capabilities of your particular terminal and so it will work even if your terminal uses a different set of escape sequences. 使用ncurses的一个优点是它将查找特定终端的功能,因此即使您的终端使用一组不同的转义序列它也能正常工作。

You have to remember that as far as the regular stdio routines are concerned, stdout is just a byte stream with no inherent display characteristics; 您必须记住,就常规stdio例程而言, stdout只是一个字节流,没有固有的显示特性; that depends on the target device, which can be anything from a regular VT100-style terminal to a hardcopy terminal to a sheet-fed printer to a plotter to whatever. 这取决于目标设备,可以是从常规VT100风格的终端到硬拷贝终端,到单张纸打印机,到绘图仪等等。

IMO, you're far better off using a library like ncurses than trying to hack together your own display management code with VT100 escape codes, even for a relatively simple task like this. IMO,使用像ncurses这样的库比使用VT100转义代码破解自己的显示管理代码好得多,即使对于这样一个相对简单的任务也是如此。 I know you want to stick with "plain old C", but this is a task that falls outside the bounds of plain old C. 我知道你想坚持使用“普通老C”,但这是一项超出普通老C范围的任务。

Use '\\r' to return to the beginning of the line and possibly rewrite the whole line. 使用'\\ r'返回行的开头并可能重写整行。

Look for VT102 control sequences - these are character sequences ESC ... to control your terminal. 寻找VT102控制序列 - 这些是字符序列ESC ...来控制你的终端。

There is also the possiblity of using Ncurses , which is a library for Textual UI, where this kind of behaviour should have some support. 还有使用Ncurses的可能性, Ncurses是Textual UI的库,这种行为应该有一些支持。 However, it may be overkill for something like this. 但是,这样的事情可能有点过分。

A slight variation on your own solution: 您自己的解决方案略有不同:

You can also print a carriage return ( \\r ), which will return you to the start of the line. 您还可以打印回车符( \\r ),它将返回到行的开头。

It is a progressbar for bash. 它是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""%";
}

About the progress bar: something like this? 关于进度条:这样的事情?

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

Note: the unistd.h and usleep thing is just to fake the progress of an operation, the progress bar code itself just uses the standard library. 注意:unistd.h和usleep的事情只是伪造一个操作的进度,进度条码本身只是使用标准库。 Its only assumptions about the output stream are that \\b actually gets to the previous written character. 它对输出流的唯一假设是\\ b实际上到达之前的书写字符。 I tried it successfully on Windows and Linux (with gnome-terminal), don't know if it doesn't work correctly with some terminal emulators. 我在Windows和Linux(使用gnome-terminal)上成功尝试过,不知道它是否与某些终端模拟器无法正常工作。 Sorry for the excessive amount of comments, I wrote it for another forum where I needed to explain pratically every line of the code to a C newbie. 对于过多的评论感到抱歉,我把它写在另一个论坛上,我需要将一行代码解释为C新手。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM