在Bash中覆盖先前的输出,而不是附加它


22

对于bash计时器,我使用以下代码:

#!/bin/bash
sek=60
echo "60 Seconds Wait!"
echo -n "One Moment please "
while [ $sek -ge 1 ]
do
   echo -n "$sek "  
sleep 1
   sek=$[$sek-1]
done
echo
echo "ready!"

那给了我类似的东西

One Moment please: 60 59 58 57 56 55 ...

是否有可能将秒的最后一个值替换为最新的值,以使输出不会产生较大的尾迹,而是像在某个位置实时显示秒倒数?(希望你明白我的意思:))


watch尽管我不确定如何执行该命令,但也许可以通过该命令执行此操作。
AJMansfield

Answers:


14
#!/bin/bash
sek=60
echo "60 Seconds Wait!"
echo -n "One Moment please "
while [ $sek -ge 1 ]
do
   echo -n "$sek" 

sleep 1
   sek=$[$sek-1]
   echo -en "\b\b"
done
echo
echo "ready!"

2
太好了,非常感谢。对于其他读者而言,这只是一个通知。要适合上面的代码,由于空间原因,必须使用echo -en“ \ b \ b \ b”。
NES 2010年

1
+1不错,但是...低于10会开始吃掉信息...
lepe 2011年

1
是的,更好用\r。看我的答案。
Mikel

3
从bash的手册中:The old format $[expression] is deprecated and will be removed in upcoming versions of bash.。请改用POSIX $((expression))((-command。EG sek=$(( sek - 1 ))(( sek = sek - 1 ))(( sek-- ))
geirha

17

基本上与aneeshep的答案相同,但是使用Return(\r)而不是Backspace(\b),因为我们不知道长度是否总是相同,例如when $sek < 10

另外,您的第一个echo应该使用$sek,而不是硬编码60

最后,请注意空格...

#!/bin/bash
sek=60
echo "$sek Seconds Wait!"
while [ $sek -ge 1 ]
do
   echo -ne "One Moment please $sek ... \r"
   sleep 1
   sek=$[$sek-1]
done
echo
echo "ready!"

7

使用bash可以使用特殊变量SECONDS

#BASH
SECONDS=0;
while sleep .5 && ((SECONDS <= 60)); do 
    printf '\r%s: %2d' "One moment please" "$((60-SECONDS))"
done
printf '\n'

+1好答案,再加上我从不知道SECONDS。
Mikel

它可以工作,但是看到“ sleep .5”是经过测试的while循环条件,这有点奇怪。然而,我特别喜欢$ SECONDS的使用,因为它与实时有关(即,两次之间的固定时间间隔不固定)与睡眠1)一样耗时的操作。... SECONDS 此变量扩展为自​​启动外壳以来的秒数。(所以我可能不会将其设置为0。从脚本启动起,仅对其进行60秒的测试).. +1
Peter.O 2011年

我要发表进一步的评论,只是因为我个人对一种获取准确的coutntdown的方式感兴趣...我已经测试了$ SECONDS,看来它是否设置为'0'还是取决于系统当前的小数秒..即 将其设置为“ 0”可导致0.99的时间(如果保持原样也是如此)。因此,其最佳平均机会仅在0.5秒以内。 ..只是一条评论(这是评论的意思:)
Peter.O 2011年

@ fred.bear好吧,您永远无法真正获得准确的结果。倒计时进行时,其他一些过程可能会开始占用CPU和/或IO,并破坏您之前的准确性。您可以做的是让它至少等待X倍的时间,并根据需要进行一些粗略的倒计时。当程序显示“需要一分钟”时,您是否希望它花费恰好1分钟(毫秒)?还是要花1分钟,花几秒钟?
geirha 2011年

@geirha ...是的,这种变化在99%的情况下都无关紧要,非常棒的是,您使我意识到了$ SECONDS ...我只是在发现其局限性...您的脚本的变体,它基于0.01秒的睡眠报告整理时间(加上您提到的任何外部系统延迟),但仅在第二次单击时才打印,但是后来我发现第一个“第二”打印得太早(在一种情况下,仅在刚开始的.01秒之后)..这都是我bash学习曲线的一部分...我喜欢您的回答,这就是为什么我对此有更深入的了解。
2011年

3

除了\r\b方法外,还可以使用\033[2K 控制字符,该字符告诉终端清除整个行。与之相比,\b这样做的优点是您不必将数字\b与要删除的字符数进行匹配,并且与之相比,\r如果新行比旧行短,则不会在屏幕上留下字符一。

下面是它如何被应用到这个问题的例子,这里是相关的应用程序创建一个类似于引导信息输出的一个例子。在此特定示例中,计时器将在到达0秒后消失,并且计时器行将替换为“ Ready!”。短语。

#!/bin/bash
sek=60
echo "60 Seconds"

while ((sek--)); do
    printf "One moment please: %d" "$sek"
    sleep 1
    printf "\r%b" "\033[2K"
done
echo "Ready!"

另一种选择是采用dialog命令在命令行中创建简单对话框。该对话框将在计时器持续时间内显示在屏幕上,并随着循环的进行而更新,直到完成为止-计时器将以无缝的方式替换为“ Ready!Press to exit”消息:

#!/bin/bash
sek=60
echo "60 Seconds"

while ((sek--)); do
    echo "$sek" | dialog --progressbox "Please wait" 10 25
    sleep 1
done
dialog --msgbox "Ready! Press <OK> to finish" 10 25

dialog在Mac上不存在:/
Ricky Levi

1

这是我在这里阅读更多内容后得出的结果:一种衬板:

SEC=101;for i in `seq $SEC -1 1`;do printf "\rNext in: %`expr length $SEC`ds" "$i";sleep 1;done;echo

更具可读性:

#!/bin/bash
SEC=101

for i in `seq $SEC -1 1`;do
        printf "\rNext in: %`expr length $SEC`ds" "$i";
        sleep 1;
done
echo

可以将SEC设置为任何正整数,并且printf将注意适当的填充。在Ubuntu和cygwin下测试。


1

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

在一行代码中,可以 echo -ne

for i in {60..1}; do echo -ne "One moment please: $i\r" && sleep 1; done

或搭配 printf

for i in {60..1}; do printf "One moment please: $i\r" && sleep 1; done

-2

倒计时器:

MIN=1 && for i in $(seq $(($MIN*60)) -1 1); do echo -n "$i, "; sleep 1; done; echo -e "nnMessage"

而“正常”秒表是:

START=$( date +%s ); while true; do CURRENT=$( date +%s ) ; echo $(( CURRENT-START )) ; sleep 1 ; echo -n  ; done

Ctrl + C停止


这不能回答有关覆盖文本的问题
Jeremy Kerr
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.