在大多数现代shell中,您可以点击向上和向下箭头,它会在提示符下放置您执行的先前命令。我的问题是,这是如何工作的?!
在我看来,shell以某种方式操纵stdout来覆盖已经写过的东西?
我注意到像wget这样的程序也是这样做的。有人知道他们是怎么做到的吗?
答案 0 :(得分:43)
它不会操纵标准输出 - 它会覆盖已经由终端显示的字符。
试试这个:
#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 library,它提供更多通用功能,包括检测终端类型,历史记录管理,可编程键绑定等。
还有一件事 - 如果有疑问,你的wget,你的shell等的来源都是可用的。
答案 1 :(得分:22)
要覆盖当前标准输出行(或部分内容),请使用\r
(或\b
。)特殊字符\r
(回车)将把插入符号返回到开头该行,允许您覆盖它。特殊字符\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);
,因为standard output is usually buffered并且信息可能不会立即打印在输出或终端上
答案 2 :(得分:12)
除了\ r和\ b之外,请查看ncurses,以便对控制台屏幕上的内容进行一些高级控制。 (包括列,任意移动等)。
答案 3 :(得分:5)
在文本终端/控制台中运行的程序可以通过各种方式操作其控制台中显示的文本(使文本变为粗体,移动光标,清除屏幕等)。这是通过打印名为"escape sequences"的特殊字符序列来实现的(因为它们通常以Escape,ASCII 27开头)。
如果stdout进入了解这些转义序列的终端,终端的显示将相应改变。
如果将stdout重定向到文件,则转义序列将出现在文件中(通常不是您想要的)。
转义序列没有完整的标准,但大多数终端使用VT100引入的序列,并有许多扩展。这就是Unix / Linux(xterm,rxvt,konsole)下的大多数终端以及PuTTY等其他终端所理解的。
在实践中,您不会直接将转义序列硬编码到您的软件中(尽管可以),但使用库来打印它们,例如上面提到的ncurses或GNU readline。这允许兼容不同的终端类型。
答案 4 :(得分:2)
已完成readline库...我不确定它是如何在幕后工作但我不认为它与stdout或流有任何关系。我怀疑readline使用某种神秘的(至少对我来说)终端命令 - 也就是说,它与实际显示你的shell会话的终端程序合作。我不知道你可以通过打印输出来获得类似readline的行为。
(考虑一下:stdout可以重定向到文件,但上/下箭头键技巧对文件不起作用。)
答案 5 :(得分:1)
您可以使用回车来模拟此事。
#include <stdio.h>
int main(int argc, char* argv[])
{
while(1)
{
printf("***********");
fflush(stdout);
sleep(1);
printf("\r");
printf("...........");
sleep(1);
}
return 0;
}
答案 6 :(得分:1)
程序通过打印终端以特殊方式解释的特殊字符来完成此操作。最简单的版本是(在大多数linux / unix终端上)将'\ r'(回车)打印到普通标准输出,它将光标位置重置为当前行中的第一个字符。所以你接下来写的东西会覆盖你之前写的那一行。例如,这可用于简单的进度指示器。
int i = 0;
while (something) {
i++;
printf("\rprocessing line %i...", i);
...
}
但是有更复杂的转义字符序列以各种方式解释。可以使用此功能完成各种操作,例如将光标定位在屏幕上的特定位置或设置文本颜色。是否或如何解释这些字符序列取决于您的终端,但大多数终端支持的公共类是ansi escape sequences。因此,如果您想要红色文字,请尝试:
printf("Text in \033[1;31mred\033[0m\n");
答案 7 :(得分:0)
最简单的方法是将stdout打印到回车符('\ r')。
光标将移动到行的开头,允许您覆盖其内容。