![如何用终端序列覆盖现有行](https://linux22.com/image/23898/%E5%A6%82%E4%BD%95%E7%94%A8%E7%BB%88%E7%AB%AF%E5%BA%8F%E5%88%97%E8%A6%86%E7%9B%96%E7%8E%B0%E6%9C%89%E8%A1%8C.png)
因此,当wget
获取网页时,它会显示一个状态栏,指示文件的下载量。它看起来像这样:
25%[=============>______________________________________] 25,000 100.0K/s
(下划线是空格;我只是不知道如何在其中获得多个连续空格)
但是,它不会向 stdout 写入另一行并添加另一个进度条,而是对其进行更新,如下所示:
50%[===========================>________________________] 50,000 100.0K/s
这也不wget
是唯一的例子。例如,当您通过管道输入某些内容less
然后退出时,原始提示符以及您之前运行的任何命令的结果仍然存在。就好像你从未离开过。
所以,我的问题是,这叫什么,我如何实现它,它一次只能用于一行吗?我可以在 C 中使用它吗?
答案1
首先,你的问题与 bash 无关,而是与终端有关。终端正在响应显示程序的文本,并且 bash 本身在程序启动后无法控制它们。
终端提供控制序列来控制颜色、字体、光标位置等。有关标准化终端序列的列表,请查看 http://www.termsys.demon.co.uk/vtansi.htm例如你可以
- 将光标置于行首
- 之后删除该行
- 写一个新行
创建进度条。
更高级的终端转义序列通常与终端相关,例如仅适用于 Eterm 或 xterm。恩诅咒- 是一个编程库,用于创建与终端的交互式程序,这样您就不必使用转义序列。
如何用终端序列覆盖现有行
echo long text
sleep 1
printf "\033[1A" # move cursor one line up
printf "\033[K" # delete till end of line
echo foo
如何在没有终端序列的情况下覆盖现有行
一个简单的解决方案是不在末尾写入换行符,而是写入回车符,这基本上将光标重置到行的开头,例如:
echo -n first
sleep 1
echo -ne "\rsecond"
echo
或 回车符\r
会将光标置于行的开头,并允许您覆盖该行的内容。
在缓冲区之间切换,如less
或vi
的行为less
也是由于更高级的终端功能,即备用屏幕:
在 VT102 模式下,有转义序列用于激活和停用备用屏幕缓冲区,该缓冲区的大小与窗口的显示区域相同。激活后,当前屏幕将被保存并替换为备用屏幕。在恢复正常屏幕之前,将禁用保存从窗口顶部滚动的行。 xterm 的 termcap(5) 条目允许可视化编辑器 vi(1) 切换到备用屏幕进行编辑并在退出时恢复屏幕。弹出菜单条目可以轻松地在正常屏幕和备用屏幕之间切换以进行剪切和粘贴。
http://rosettacode.org/wiki/Terminal_control/Preserve_screen列出了一些如何自己操作的示例,可以通过输出或通过一些转义序列。
答案2
使用-- 回车符将光标发送到当前行的开头,而不是使用echo
自动将换行符附加到字符串。printf "%s\r" whatever
例子:
seq 1 15 | while read num; do printf "%2d\r" $num; sleep 1; done; echo ""
答案3
我还建议那些发现这个主题的人看看Bash 提示符 HOWTO - 光标移动。
例子:
- Position the Cursor:
\033[<L>;<C>H
Or
\033[<L>;<C>f
puts the cursor at line L and column C.
- Move the cursor up N lines:
\033[<N>A
- Move the cursor down N lines:
\033[<N>B
- Move the cursor forward N columns:
\033[<N>C
- Move the cursor backward N columns:
\033[<N>D
- Clear the screen, move to (0,0):
\033[2J
- Erase to end of line:
\033[K
- Save cursor position:
\033[s
- Restore cursor position:
\033[u
C 中的一些例子:
void saveCursorPosition() {
printf("\033[s");
}
void restoreCursorPosition() {
printf("\033[u");
}
void lineUP(short int times) {
printf("\033[%iA", times);
}
void lineDown(short int times) {
printf("\033[%iB", times);
}
程序示例:
#include <stdio.h>
#include <unistd.h>
void saveCursorPosition() {
printf("\033[s");
}
void restoreCursorPosition() {
printf("\033[u");
}
void lineUP(short int times) {
printf("\033[%iA", times);
}
void moveCursorBackwards(short int times) {
printf("\033[%iD", times);
}
void printMainText() {
printf("\n ╔═══════════════════════════════╗");
printf("\n ║ ║");
printf("\n ║ Progress Bar ║");
printf("\n ║ ║");
printf("\n ║ [] ║");
printf("\n ║ ║");
printf("\n ║ Press Ctrl+C to close ║");
printf("\n ║ ║");
printf("\n ╚═══════════════════════════════╝\n");
}
int main(int argc, char **argv) {
printMainText();
for (int progress=0; progress <= 10; progress++) {
saveCursorPosition();
lineUP(5);
printf("\r ║ [");
fflush(stdout);
for (int i=0; i<progress; i++) {
printf("=>]");
fflush(stdout);
moveCursorBackwards(2);
}
moveCursorBackwards(progress + 5);
printf("%i%%", progress * 10);
fflush(stdout);
restoreCursorPosition();
sleep(1);
}
return 0;
}