在大多数现代shell中,您可以点击向上和向下箭头,它会在提示符下放置您执行的先前命令.我的问题是,这是如何工作的?!
在我看来,shell以某种方式操纵stdout来覆盖它已经编写的内容?
我注意到像wget这样的程序也可以这样做.有人知道他们是怎么做到的吗?
它不是在操纵标准输出 - 它会覆盖已经由终端显示的字符.
试试这个:
#include#include 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等的来源都是可用的.
要覆盖当前标准输出行(或其中的一部分),请使用\r
(或\b
.)特殊字符\r
(回车符)将把插入符号返回到行的开头,允许您覆盖它.特殊字符\b
只会使插入符号返回一个位置,允许您覆盖最后一个字符,例如
#include#include 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);
是因为标准输出通常是缓冲的,否则信息不会立即打印在输出或终端上
除了\ r和\ b之外,还可以看看ncurses对控制台屏幕上的内容进行一些高级控制.(包括列,任意移动等).
在文本终端/控制台中运行的程序可以以各种方式操作其控制台中显示的文本(使文本变为粗体,移动光标,清除屏幕等).这是通过打印称为"转义序列"的特殊字符序列来实现的(因为它们通常以Escape,ASCII 27开头).
如果stdout进入了解这些转义序列的终端,则终端的显示将相应地改变.
如果将stdout重定向到文件,则转义序列将出现在文件中(通常不是您想要的).
转义序列没有完整的标准,但大多数终端使用VT100引入的序列,有许多扩展.这就是Unix/Linux下的大多数终端(xterm,rxvt,konsole)和其他像PuTTY这样的终端.
在实践中,您不会直接将转义序列硬编码到您的软件中(尽管您可以),但是使用库来打印它们,例如上面提到的ncurses或GNU readline.这允许与不同终端类型兼容.