如何在C中覆盖stdout

时间:2022-07-01 05:49:20

In most modern shells, you can hit the up and down arrows and it will put, at the prompt, previous commands that you have executed. My question is, how does this work?!

在大多数现代shell中,您可以点击向上和向下箭头,它会在提示符下放置您执行的先前命令。我的问题是,这是如何工作的?!

It seems to me that the shell is somehow manipulating stdout to overwrite what it has already written?

在我看来,shell以某种方式操纵stdout来覆盖它已经编写的内容?

I notice that programs like wget do this as well. Does anybody have any idea how they do it?

我注意到像wget这样的程序也是这样做的。有人知道他们是怎么做到的吗?

8 个解决方案

#1


41  

It's not manipulating stdout -- it's overwriting the characters which have already been displayed by the terminal.

它不是在操纵标准输出 - 它会覆盖已经由终端显示的字符。

Try this:

尝试这个:

#include <stdio.h>
#include <unistd.h>
static char bar[] = "======================================="
                    "======================================>";
int main() {
    int i;
    for (i = 77; i >= 0; i--) {
        printf("[%s]\r", &bar[i]);
        fflush(stdout);
        sleep(1);
    }
    printf("\n");
    return 0;
}

That's pretty close to wget's output, right? \r is a carriage-return, which the terminal interprets as "move the cursor back to the start of the current line".

这与wget的输出非常接近,对吧? \ r \ n是一个回车符,终端将其解释为“将光标移回当前行的开头”。

Your shell, if it's bash, uses the GNU Readline library, which provides much more general functionality, including detecting terminal types, history management, programmable key bindings, etc.

你的shell,如果它是bash,使用GNU Readline库,它提供了更多的一般功能,包括检测终端类型,历史管理,可编程键绑定等。

One more thing -- when in doubt, the source for your wget, your shell, etc. are all available.

还有一件事 - 如果有疑问,你的wget,你的shell等的来源都是可用的。

#2


22  

To overwrite the current standard output line (or parts of it) use \r (or \b.) The special character \r (carriage return) will return the caret to the beginning of the line, allowing you to overwrite it. The special character \b will bring the caret back one position only, allowing you to overwrite the last character, e.g.

要覆盖当前标准输出行(或部分输出行),请使用\ r \ n(或\ b。)特殊字符\ r \ n(回车符)会将插入符号返回到行的开头,允许您覆盖它。特殊字符\ b只会将插入符号移回一个位置,允许您覆盖最后一个字符,例如

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

int i;
const char progress[] = "|/-\\";

for (i = 0; i < 100; i += 10) {
  printf("Processing: %3d%%\r",i); /* \r returns the caret to the line start */
  fflush(stdout);
  sleep(1);
}
printf("\n"); /* goes to the next line */
fflush(stdout);

printf("Processing: ");
for (i = 0; i < 100; i += 10) {
  printf("%c\b", progress[(i/10)%sizeof(progress)]); /* \b goes one back */
  fflush(stdout);
  sleep(1);
}
printf("\n"); /* goes to the next line */
fflush(stdout);

Use fflush(stdout); because standard output is usually buffered and the information may not otherwise be immediately printed on the output or terminal

使用fflush(stdout);因为标准输出通常是缓冲的,否则信息不会立即打印在输出或终端上

#3


11  

In addition to \r and \b, take a look at ncurses for some advanced control over what's on the console screen. (Including columns, moving around arbitrarily, etc).

除了\ r和\ b之外,请查看ncurses,以便对控制台屏幕上的内容进行一些高级控制。 (包括列,任意移动等)。

#4


5  

A program running in a text terminal / console can manipulate the text displayed in its console in various ways (make text bold, move cursor, clear screen etc.). This is accomplished by printing special character sequences, called "escape sequences" (because they usually start with Escape, ASCII 27).

在文本终端/控制台中运行的程序可以以各种方式操作其控制台中显示的文本(使文本变为粗体,移动光标,清除屏幕等)。这是通过打印称为“转义序列”的特殊字符序列来实现的(因为它们通常以Escape,ASCII 27开头)。

If stdout goes to a terminal which understands these escape sequences, the display of the terminal will change accordingly.

如果stdout进入了解这些转义序列的终端,则终端的显示将相应地改变。

If you redirect stdout to a file, the escape sequences will appear in the file (which is usually not what you want).

如果将stdout重定向到文件,则转义序列将出现在文件中(通常不是您想要的)。

There is no complete standard for escape sequences, but most terminals use the sequences introduced by VT100, with many extensions. This is what most terminals under Unix/Linux (xterm, rxvt, konsole) and others like PuTTY understand.

转义序列没有完整的标准,但大多数终端使用VT100引入的序列,具有许多扩展。这就是Unix / Linux下的大多数终端(xterm,rxvt,konsole)和其他像PuTTY这样的终端。

In practice, you would not directly hardcode escape sequences into your software (though you could), but use a library to print them, such as ncurses or GNU readline mentioned above. This allows compatibility with different terminal types.

在实践中,您不会直接将转义序列硬编码到您的软件中(尽管您可以),但是使用库来打印它们,例如上面提到的ncurses或GNU readline。这允许与不同终端类型兼容。

#5


2  

It's done with the readline library... I'm not sure how it works behind the scenes but I don't think it has anything to do with stdout or streams. I suspect readline uses some sort of cryptic (to me, at least) terminal commands - that is, it cooperates with the terminal program that actually displays your shell session. I don't know that you can get readline-like behavior just by printing output.

它是用readline库完成的......我不确定它在幕后是如何工作的,但我不认为它与stdout或流有任何关系。我怀疑readline使用某种神秘的(至少对我来说)终端命令 - 也就是说,它与实际显示你的shell会话的终端程序合作。我不知道你可以通过打印输出来获得类似readline的行为。

(Think about this: stdout can be redirected to a file, but the up/down-arrow keys trick doesn't work on files.)

(想想看:stdout可以重定向到文件,但上/下箭头键技巧对文件不起作用。)

#6


1  

You can use carriage return to simulate this.

您可以使用回车来模拟这个。

#include <stdio.h>

int main(int argc, char* argv[])
{
    while(1)
    {
        printf("***********");
        fflush(stdout);
        sleep(1);
        printf("\r");
        printf("...........");
        sleep(1);
    }

    return 0;
}

#7


1  

The program does this by printing special characters that the terminal interprets in a special way. The most simple version of this is (on most linux/unix terminals) to print '\r' (carriage return) to the normal stdout which resets the cursor position to the first character in the current line. So the thing you write next will overwrite the line you wrote previously. This can be used for simple progress indicators, for example.

该程序通过打印终端以特殊方式解释的特殊字符来完成此操作。最简单的版本是(在大多数linux / unix终端上)将'\ r'(回车)打印到普通标准输出,它将光标位置重置为当前行中的第一个字符。所以你接下来写的东西会覆盖你之前写的那一行。例如,这可用于简单的进度指示器。

int i = 0;
while (something) {
  i++;
  printf("\rprocessing line %i...", i);
  ...
}

But there are more complicated escape characters sequences that are interpreted in various ways. All kinds of things can be done with this, like positioning the cursor at a specific position on the screen or setting the text color. If or how these character sequences are interpreted depends on your terminal, but a common class supported by most terminals are ansi escape sequences. So if you want red text, try:

但是有更复杂的转义字符序列以各种方式解释。可以使用此功能完成各种操作,例如将光标定位在屏幕上的特定位置或设置文本颜色。是否或如何解释这些字符序列取决于您的终端,但大多数终端支持的公共类是ansi转义序列。因此,如果您想要红色文字,请尝试:

printf("Text in \033[1;31mred\033[0m\n");

#8


0  

The simplest way is to print to stdout the carriage return character ('\r').

最简单的方法是打印到stdout回车符('\ r')。

The cursor will be moved to the start of the line, allowing you to overwrite its contents.

光标将移动到行的开头,允许您覆盖其内容。

#1


41  

It's not manipulating stdout -- it's overwriting the characters which have already been displayed by the terminal.

它不是在操纵标准输出 - 它会覆盖已经由终端显示的字符。

Try this:

尝试这个:

#include <stdio.h>
#include <unistd.h>
static char bar[] = "======================================="
                    "======================================>";
int main() {
    int i;
    for (i = 77; i >= 0; i--) {
        printf("[%s]\r", &bar[i]);
        fflush(stdout);
        sleep(1);
    }
    printf("\n");
    return 0;
}

That's pretty close to wget's output, right? \r is a carriage-return, which the terminal interprets as "move the cursor back to the start of the current line".

这与wget的输出非常接近,对吧? \ r \ n是一个回车符,终端将其解释为“将光标移回当前行的开头”。

Your shell, if it's bash, uses the GNU Readline library, which provides much more general functionality, including detecting terminal types, history management, programmable key bindings, etc.

你的shell,如果它是bash,使用GNU Readline库,它提供了更多的一般功能,包括检测终端类型,历史管理,可编程键绑定等。

One more thing -- when in doubt, the source for your wget, your shell, etc. are all available.

还有一件事 - 如果有疑问,你的wget,你的shell等的来源都是可用的。

#2


22  

To overwrite the current standard output line (or parts of it) use \r (or \b.) The special character \r (carriage return) will return the caret to the beginning of the line, allowing you to overwrite it. The special character \b will bring the caret back one position only, allowing you to overwrite the last character, e.g.

要覆盖当前标准输出行(或部分输出行),请使用\ r \ n(或\ b。)特殊字符\ r \ n(回车符)会将插入符号返回到行的开头,允许您覆盖它。特殊字符\ b只会将插入符号移回一个位置,允许您覆盖最后一个字符,例如

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

int i;
const char progress[] = "|/-\\";

for (i = 0; i < 100; i += 10) {
  printf("Processing: %3d%%\r",i); /* \r returns the caret to the line start */
  fflush(stdout);
  sleep(1);
}
printf("\n"); /* goes to the next line */
fflush(stdout);

printf("Processing: ");
for (i = 0; i < 100; i += 10) {
  printf("%c\b", progress[(i/10)%sizeof(progress)]); /* \b goes one back */
  fflush(stdout);
  sleep(1);
}
printf("\n"); /* goes to the next line */
fflush(stdout);

Use fflush(stdout); because standard output is usually buffered and the information may not otherwise be immediately printed on the output or terminal

使用fflush(stdout);因为标准输出通常是缓冲的,否则信息不会立即打印在输出或终端上

#3


11  

In addition to \r and \b, take a look at ncurses for some advanced control over what's on the console screen. (Including columns, moving around arbitrarily, etc).

除了\ r和\ b之外,请查看ncurses,以便对控制台屏幕上的内容进行一些高级控制。 (包括列,任意移动等)。

#4


5  

A program running in a text terminal / console can manipulate the text displayed in its console in various ways (make text bold, move cursor, clear screen etc.). This is accomplished by printing special character sequences, called "escape sequences" (because they usually start with Escape, ASCII 27).

在文本终端/控制台中运行的程序可以以各种方式操作其控制台中显示的文本(使文本变为粗体,移动光标,清除屏幕等)。这是通过打印称为“转义序列”的特殊字符序列来实现的(因为它们通常以Escape,ASCII 27开头)。

If stdout goes to a terminal which understands these escape sequences, the display of the terminal will change accordingly.

如果stdout进入了解这些转义序列的终端,则终端的显示将相应地改变。

If you redirect stdout to a file, the escape sequences will appear in the file (which is usually not what you want).

如果将stdout重定向到文件,则转义序列将出现在文件中(通常不是您想要的)。

There is no complete standard for escape sequences, but most terminals use the sequences introduced by VT100, with many extensions. This is what most terminals under Unix/Linux (xterm, rxvt, konsole) and others like PuTTY understand.

转义序列没有完整的标准,但大多数终端使用VT100引入的序列,具有许多扩展。这就是Unix / Linux下的大多数终端(xterm,rxvt,konsole)和其他像PuTTY这样的终端。

In practice, you would not directly hardcode escape sequences into your software (though you could), but use a library to print them, such as ncurses or GNU readline mentioned above. This allows compatibility with different terminal types.

在实践中,您不会直接将转义序列硬编码到您的软件中(尽管您可以),但是使用库来打印它们,例如上面提到的ncurses或GNU readline。这允许与不同终端类型兼容。

#5


2  

It's done with the readline library... I'm not sure how it works behind the scenes but I don't think it has anything to do with stdout or streams. I suspect readline uses some sort of cryptic (to me, at least) terminal commands - that is, it cooperates with the terminal program that actually displays your shell session. I don't know that you can get readline-like behavior just by printing output.

它是用readline库完成的......我不确定它在幕后是如何工作的,但我不认为它与stdout或流有任何关系。我怀疑readline使用某种神秘的(至少对我来说)终端命令 - 也就是说,它与实际显示你的shell会话的终端程序合作。我不知道你可以通过打印输出来获得类似readline的行为。

(Think about this: stdout can be redirected to a file, but the up/down-arrow keys trick doesn't work on files.)

(想想看:stdout可以重定向到文件,但上/下箭头键技巧对文件不起作用。)

#6


1  

You can use carriage return to simulate this.

您可以使用回车来模拟这个。

#include <stdio.h>

int main(int argc, char* argv[])
{
    while(1)
    {
        printf("***********");
        fflush(stdout);
        sleep(1);
        printf("\r");
        printf("...........");
        sleep(1);
    }

    return 0;
}

#7


1  

The program does this by printing special characters that the terminal interprets in a special way. The most simple version of this is (on most linux/unix terminals) to print '\r' (carriage return) to the normal stdout which resets the cursor position to the first character in the current line. So the thing you write next will overwrite the line you wrote previously. This can be used for simple progress indicators, for example.

该程序通过打印终端以特殊方式解释的特殊字符来完成此操作。最简单的版本是(在大多数linux / unix终端上)将'\ r'(回车)打印到普通标准输出,它将光标位置重置为当前行中的第一个字符。所以你接下来写的东西会覆盖你之前写的那一行。例如,这可用于简单的进度指示器。

int i = 0;
while (something) {
  i++;
  printf("\rprocessing line %i...", i);
  ...
}

But there are more complicated escape characters sequences that are interpreted in various ways. All kinds of things can be done with this, like positioning the cursor at a specific position on the screen or setting the text color. If or how these character sequences are interpreted depends on your terminal, but a common class supported by most terminals are ansi escape sequences. So if you want red text, try:

但是有更复杂的转义字符序列以各种方式解释。可以使用此功能完成各种操作,例如将光标定位在屏幕上的特定位置或设置文本颜色。是否或如何解释这些字符序列取决于您的终端,但大多数终端支持的公共类是ansi转义序列。因此,如果您想要红色文字,请尝试:

printf("Text in \033[1;31mred\033[0m\n");

#8


0  

The simplest way is to print to stdout the carriage return character ('\r').

最简单的方法是打印到stdout回车符('\ r')。

The cursor will be moved to the start of the line, allowing you to overwrite its contents.

光标将移动到行的开头,允许您覆盖其内容。