Bash - 正确清除最后一个输出

标签 bash tput

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

但是,如果输出足够长以致于强制终端向上滚动,这将失败。发生这种情况时,保存的光标位置不再正确,并且无法正确清除最后的输出。

例如,如果光标当前在终端的底部并且输出比终端宽度长,则会强制终端向上滚动,使之前保存的光标位置无效。

那么有没有什么办法可以保证Bash中的光标永远不会到终端结束呢?或者也许还有其他一些替代方法来防止这个问题?

编辑:我根据 F. Hauri 的 answer 制作了自己的版本,简化了我的用例
#!/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

最佳答案

有点棘手

灵感来自 How to get the cursor position in 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参数将被重新计算。

更复杂的样本
  • 使用 trap WINCH仅在窗口调整大小时查询终端大小
  • 添加 newlines用于向上滚动 cuu
  • 减少货叉到 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'}"
    

    关于Bash - 正确清除最后一个输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62038398/

    相关文章:

    bash - 对于查看 .tgz 文件中的源文件有什么建议吗?

    bash - 使用 tput 时如何在 bash 中使用 printf 格式化列

    linux - 哪个开源程序类似于Linux "dialog"命令?

    linux - 酒保+用红色标记失败/开始的备份

    linux - 使用管道在 awk 语句中输入

    node.js - npm 脚本忽略 grep 命令的参数

    bash - 停止执行 makefile

    regex - 删除除 () 包围之外的所有内容