如何使用bash在终端中删除和替换最后一行?


99

我想实现一个进度条,以bash显示经过的秒数。为此,我需要擦除屏幕上显示的最后一行(命令“ clear”将擦除所有屏幕,但是我只需要擦除进度条的那一行并将其替换为新信息)。

最终结果应如下所示:

$ Elapsed time 5 seconds

然后在10秒钟后,我想用(在屏幕上的相同位置)替换此句子:

$ Elapsed time 15 seconds

Answers:


116

用\ r回车

seq 1 1000000 | while read i; do echo -en "\r$i"; done

来自人的回声:

-n     do not output the trailing newline
-e     enable interpretation of backslash escapes

\r     carriage return

19
for i in {1..100000}; do echo -en "\r$i"; done避免后续呼叫:-)
道格拉斯·里德

这正是我需要的,谢谢!而且seq命令的使用确实冻结了termianl :-)
调试器2010年

11
当您使用诸如“用于$(...)中的i”或“用于{1..N}中的i”之类的东西时,会在迭代之前生成所有元素,这对于大型输入而言效率很低。您可以利用管道:“ seq 1 100000 |同时读取i;执行...”,也可以使用bash c风格的循环:“用于((i = 0 ;; i ++));执行...”
tokland 2010年

谢谢道格拉斯和托克兰-尽管序列产生并不是直接问题的一部分,但我已改为托克兰效率更高的烟斗

1
我的矩阵打印机完全弄乱了我的纸张。它会在不再存在的同一张纸上留下卡纸点,该程序运行多长时间?
罗布2015年

185

回车本身仅会将光标移动到行的开头。如果每行新输出至少与上一行一样长就可以,但是如果新行较短,则前一行将不会被完全覆盖,例如:

$ echo -e "abcdefghijklmnopqrstuvwxyz\r0123456789"
0123456789klmnopqrstuvwxyz

要实际清除新文本的行,您可以在\033[K后面添加\r

$ echo -e "abcdefghijklmnopqrstuvwxyz\r\033[K0123456789"
0123456789

http://en.wikipedia.org/wiki/ANSI_escape_code


3
这在我的环境中确实很好用。有兼容性知识吗?
亚历山大·奥尔森

21
在Bash中,至少可以缩短为\e[K而不是\033[K
戴夫·詹姆斯·米勒

好答案!使用红宝石看跌期权可以完美地工作。现在是我工具箱的一部分。
phatmann 2012年

@The Pixel Developer:感谢您尝试改进它,但是您的编辑不正确。返回必须首先发生,将光标移动到行的开头,然后执行kill清除从该光标位置到结尾的所有内容,使整行留空,这就是目的。您可以将它们放在每行的开头,与Ken的答案类似,以便将其写在空白行上。
Derek Veit

1
仅作记录,一个人也可以做\033[Gio \r\033[K尽管显然\r要简单得多。另外,invisible-island.net / xterm / ctlseqs / ctlseqs.html比Wikipedia提供了更多详细信息,该信息来自xterm开发人员。
jamadagni

19

只要线的长度不超过端子的宽度,Derek Veit的答案就行得通。如果不是这种情况,下面的代码将阻止垃圾输出:

在第一次编写该行之前,请执行

tput sc

保存当前光标位置。现在,每当您要打印行时,请使用

tput rc
tput ed
echo "your stuff here"

首先返回到保存的光标位置,然后从光标到底部清除屏幕,最后写入输出。


奇怪的是,这在终结器中什么也没做。您是否知道兼容性限制?
Jamie Pate 2014年

1
Cygwin的注意事项:您需要安装软件包“ ncurses”才能使用“ tput”。
杰克·米勒

嗯...如果输出中多于一行,则似乎不起作用。这不起作用:tput sc # save cursor echo '' > sessions.log.json while [ 1 ]; do curl 'http://localhost/jolokia/read/*:type=Manager,*/activeSessions,maxActiveSessions' >> sessions.log.json echo '' >> sessions.log.json cat sessions.log.json | jq '.' tput rc;tput el # rc = restore cursor, el = erase to end of line sleep 1 done
Nux

@Nux您需要使用tput ed而不是tput el。@Um的示例已使用ed(也许他在您评论后将其修复了)。
studgeek

仅供参考,这是tput命令的列表tput的颜色和光标移动
studgeek

12

\ 033方法对我不起作用。\ r方法有效,但实际上并不会删除任何内容,只是将光标置于行的开头。因此,如果新字符串比旧字符串短,那么您可以在行尾看到剩余的文本。最终,tput是最好的方法。除了游标之外,它还有其他用途,而且它已预装在许多Linux和BSD发行版中,因此它应可用于大多数bash用户。

#/bin/bash
tput sc # save cursor
printf "Something that I made up for this string"
sleep 1
tput rc;tput el # rc = restore cursor, el = erase to end of line
printf "Another message for testing"
sleep 1
tput rc;tput el
printf "Yet another one"
sleep 1
tput rc;tput el

这是一个倒计时脚本:

#!/bin/bash
timeout () {
    tput sc
    time=$1; while [ $time -ge 0 ]; do
        tput rc; tput el
        printf "$2" $time
        ((time--))
        sleep 1
    done
    tput rc; tput ed;
}

timeout 10 "Self-destructing in %s"

那确实清除了整条线,问题对我来说闪烁得太多了:'(
smarber

5

如果进度输出是多行,或者脚本已经打印了新行字符,则可以使用以下内容向上跳行:

printf "\033[5A"

这会使光标向上跳5行。然后,您可以覆盖所需的任何内容。

如果那行不通,您可以尝试printf "\e[5A"echo -e "\033[5A",它应该具有相同的效果。

基本上,使用转义序列,您可以控制屏幕中的几乎所有内容。


可移植的等效项是tput cuu 5,其中5是行数(cuu上移,cud下移)。
梅兰

4

使用回车符:

echo -e "Foo\rBar" # Will print "Bar"

这个答案是最简单的方法。您甚至可以使用以下方法达到相同的效果:printf“ Foo \ rBar”
lee8oi 2015年

1

可以通过放置回车符来实现\r

在一行代码中 printf

for i in {10..1}; do printf "Counting down: $i\r" && sleep 1; done

或搭配 echo -ne

for i in {10..1}; do echo -ne "Counting down: $i\r" && sleep 1; done
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.