重击-正确清除最后一个输出 [英] Bash - Clearing the last output correctly
问题描述
我正在尝试创建可更新的进度状态.为了做到这一点,我需要能够全部清除最后一个输出,以便可以对其进行更新.回车可以工作,但是当输出的长度大于端子宽度并回绕时,它将无法清除最后的输出. 所以我正在使用tput:
I'm trying to create an update-able progress status. In order to do that, I need to be able to clear the last output in its entirety so that I can update it. Carriage returns can work, but when the output is longer than the terminal width and wraps around, it will fail to clear the last output. So I'm using tput:
n=0
while [[ $n -ne 100 ]]; do
n=$((n+1))
tput ed #clear
tput sc #save cursor
echo -n "Progress: ${n}%"
tput rc #restore cursor
sleep 1s
done
echo
但是,如果输出足够长以至于迫使终端向上滚动,则此操作将失败.发生这种情况时,保存的光标位置将不再正确,并且将无法正确清除最后的输出.
But this will fail if the output is long enough that it forces the terminal to scroll up. When that happens, the saved cursor position is no longer correct and it will fail to clear the last output correctly.
例如,如果光标当前位于终端的底部,并且输出的长度大于终端的宽度,则会迫使终端向上滚动,从而使先前保存的光标位置无效.
For example, if the cursor is currently at the bottom of the terminal and the output is longer than the terminal width, it will force the terminal to scroll up, invalidating the previously saved cursor position.
那么有什么方法可以确保游标永远不会在Bash中终止于终端?还是其他一些预防此问题的方法?
So are there any ways to ensure that the cursor will never the end of the terminal in Bash? Or maybe some other alternative methods to prevent this problem?
我根据F. Hauri的 answer 制作了自己的版本,对我的用例进行了简化
I made my own version based on F. Hauri's answer, simplified for my use case
#!/bin/bash
str=$(head -c 338 < /dev/zero | tr '\0' '\141')
len="${#str}"
col=$(tput cols)
lines=$(( ((len + col - 1) / col) - 1 ))
echo -ne "${str}\r"
(( len > col )) && tput cuu "$lines"
sleep 3s
tput ed
推荐答案
有些棘手的
#!/bin/bash
lineformat="This is a very long line with a lot of stuff so they will take "
lineformat+="more than standard terminal width (80) columns... Progress %3d%%"
n=0
while [[ $n -ne 100 ]]; do
n=$((n+1))
printf -v outputstring "$lineformat" $n
twidth=$(tput cols) # Get terminal width
theight=$(tput lines) # Get terminal height
oldstty=$(stty -g) # Save stty settings
stty raw -echo min 0 # Suppres echo on terminal
# echo -en "\E[6n"
tput u7 # Inquire for cursor position
read -sdR CURPOS # Read cursor position
stty $oldstty # Restore stty settings
IFS=\; read cv ch <<<"${CURPOS#$'\e['}" # split $CURPOS
uplines=$(((${#outputstring}/twidth)+cv-theight))
((uplines>0)) &&
tput cuu $uplines # cursor up one or more lines
tput ed # clear to end of screen
tput sc # save cursor position
echo -n "$outputstring"
tput rc # restore cursor
sleep .0331s
done
echo
在每个循环中启动tput cols
和tput lines
时,您可以在运行时调整窗口大小,然后重新计算cuu
参数.
As tput cols
and tput lines
is initiated at each loop, you could resize window while running, cuu
argument will be re-computed.
- 仅在调整窗口大小时才使用
trap WINCH
查询终端大小 - Addind
newlines
用于在cuu
之前向上滚动
- 将 forks 减少为
tput
- Using
trap WINCH
for querying terminal size only when window is resized - Addind
newlines
for scrolling up beforecuu
- Reducing forks to
tput
有:
#!/bin/bash
lineformat="This is a very long line with a lot of stuff so they will take "
lineformat+="more than standard terminal width (80) columns... Progress %3d%%"
getWinSize() {
{
read twidth
read theight
} < <(
tput -S - <<<$'cols\nlines'
)
}
trap getWinSize WINCH
getWinSize
getCpos=$(tput u7)
getCurPos() {
stty raw -echo min 0
echo -en "$getCpos"
read -sdR CURPOS
stty $oldstty
IFS=\; read curv curh <<<"${CURPOS#$'\e['}"
}
oldstty=$(stty -g)
before=$(tput -S - <<<$'ed\nsc')
after=$(tput rc)
n=0
while [[ $n -ne 100 ]]; do
n=$((n+1))
printf -v outputstring "$lineformat" $n
getCurPos
uplines=$(((${#outputstring}/twidth)+curv-theight))
if ((uplines>0)) ;then
printf -v movedown "%${uplines}s" ''
echo -en "${movedown// /\\n}"
tput cuu $uplines
fi
printf "%s%s%s" "$before" "$outputstring" "$after"
sleep .05
done
downlines=$((${#outputstring}/twidth))
printf -v movedown "%${downlines}s" ''
echo "${movedown// /$'\n'}"
这篇关于重击-正确清除最后一个输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!