Как распечатать в той же строке, переопределяя предыдущую строку?

Иногда я вижу некоторые команды в терминале, которые печатают результаты в stdout, но в той же строке. Например, wget печатает стрелку, как показано ниже:

0[=>        ]100%
0[  =>      ]100%
0[    =>    ]100%
0[      =>  ]100%
0[        =>]100%

но он распечатывается в той же строке, что и стрелка. Как я могу достичь того же в моих программах, используя bash или sh? Нужно ли использовать другие инструменты?

UPDATE:

Я знаю, что я упомянул wget, который по умолчанию работает в Linux, на основе GNU... Есть ли общий подход, который работает и для BSD? (например, OSX) → ОК, если я использую bash вместо sh, то он работает:)

Ответ 1

Используйте специальный символ \r. Он возвращается в начало строки, не переходя к следующему.

for i in {1..10} ; do
    echo -n '['
    for ((j=0; j<i; j++)) ; do echo -n ' '; done
    echo -n '=>'
    for ((j=i; j<10; j++)) ; do echo -n ' '; done
    echo -n "] $i"0% $'\r'
    sleep 1
done

Ответ 2

Вы можете использовать \r для этой цели:

Например, это будет продолжать обновлять текущее время в той же строке:

while true; do echo -ne "$(date)\r"; done

Ответ 3

Вы также можете использовать ANSI/VT100 terminal escape sequences для достижения этой цели.

Вот краткий пример. Конечно, вы можете комбинировать операторы printf с одним.

#!/bin/bash

MAX=60
ARR=( $(eval echo {1..${MAX}}) )

for i in ${ARR[*]} ; do 
    # delete from the current position to the start of the line
    printf "\e[2K"
    # print '[' and place '=>' at the $i'th column
    printf "[\e[%uC=>" ${i}
    # place trailing ']' at the ($MAX+1-$i)'th column
    printf "\e[%uC]" $((${MAX}+1-${i}))
    # print trailing '100%' and move the cursor one row up
    printf " 100%% \e[1A\n"

    sleep 0.1
done

printf "\n"

С помощью управляющих последовательностей вы больше всего контролируете свой экран терминала.

Вы можете найти обзор возможных последовательностей в [1].

[1] http://ascii-table.com/ansi-escape-sequences-vt-100.php