重击-正确清除最后一个输出 [英] Bash - Clearing the last output correctly

查看:84
本文介绍了重击-正确清除最后一个输出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建可更新的进度状态.为了做到这一点,我需要能够全部清除最后一个输出,以便可以对其进行更新.回车可以工作,但是当输出的长度大于端子宽度并回绕时,它将无法清除最后的输出. 所以我正在使用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

推荐答案

有些棘手的

如何在bash中获取光标位置的启发?

#!/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 colstput 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 before cuu
  • 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆