如何在 C 中覆盖 stdout

发布于 2024-07-15 21:58:37 字数 168 浏览 11 评论 0原文

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

在我看来,shell 正在以某种方式操纵标准输出来覆盖它已经写入的内容?

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

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?!

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

禾厶谷欠 2024-07-22 21:58:38

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

#include <stdio.h>

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

    return 0;
}

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;
}
落花浅忆 2024-07-22 21:58:38

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

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

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

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

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.

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:

printf("Text in \033[1;31mred\033[0m\n");
千秋岁 2024-07-22 21:58:38

最简单的方法是将回车符 ('\r') 打印到标准输出。

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

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

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

野生奥特曼 2024-07-22 21:58:37

它不是操纵标准输出——而是覆盖终端已经显示的字符。

试试这个:

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

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

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

还有一件事——当有疑问时,wget、shell 等的源代码都是可用的。

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".

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.

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

红尘作伴 2024-07-22 21:58:37

要覆盖当前标准输出行(或其一部分),请使用 \r (或 \b)。特殊字符 \r (回车return) 会将插入符号返回到行的开头,允许您覆盖它。 特殊字符 \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);

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

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.

#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

小…红帽 2024-07-22 21:58:37

除了 \r 和 \b 之外,还可以查看 ncurses 以获取一些高级控制控制台屏幕上有什么。 (包括列、任意移动等)。

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).

北座城市 2024-07-22 21:58:37

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

如果 stdout 转到理解这些转义序列的终端,则终端的显示将相应改变。

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

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

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

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).

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

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

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.

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.

染墨丶若流云 2024-07-22 21:58:37

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

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

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.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文