it-roy-ru.com

Перезаписать последнюю строку в терминале

Мой bash-скрипт выглядит следующим образом:

echo "Description:"
while [ $finishInput -eq 0 ]; do
read tmp
desc="$desc"$'\n'"$tmp"
if [ -z "$tmp" ]; then
finishInput="1"
fi
done
echo -n "Maintainer:"
read maintainer

Он читает в desc var, пока не будет передана пустая строка. После этого я хочу читать в других материалах.

При выполнении моего текущего скрипта это выглядит так:

Description:
Line 1
Line 2

Maintainer:

Я хотел бы перезаписать последнюю пустую строку с помощью "Maintainer:".

Я искал решение, но нашел только предложения, которые были похожи

echo -n "Old line"
echo -e "\r new line"

который остается на линии и перезаписывает его. Это не возможно в моем случае.

42
Zulakis

Просто используйте "\e[1A".

В вашем примере вы удаляете текст в той же строке:

$ echo -n "Old line"; echo -e "\e[0K\r new line"
 new line

Если вы хотите вернуться к предыдущей строке, используйте\e [1A:

$ echo "Old line"; echo -en "\e[1A"; echo -e "\e[0K\r new line"
 new line

Если вы хотите перейти на N строк вверх, используйте \e[<N>A.

64
Igor Chubin

Я построил функцию от Комментарий Денниса Уильямсона :

function clearLastLine() {
        tput cuu 1 && tput el
}

Спасибо Деннису Уильямсону

11
Murmel

Нашел отличное руководство по escape-последовательностям и хотел бы подробнее остановиться на некоторых обсуждениях здесь. 

Когда вы пишете в терминал, вы перемещаете невидимый курсор, как и в любом текстовом редакторе. При использовании echo он автоматически заканчивает вывод символом новой строки, который перемещает курсор на следующую строку.

$ echo "Hello" && echo " World"
Hello
 World

Вы можете использовать -n, чтобы запретить новую строку, и если после этого вы повторите эхо, он добавит его в конец этой строки

$ echo -n "Hello" && echo " World"
Hello World

Курсор остается там, где он был, поэтому сам по себе мы не можем использовать -n для перезаписи предыдущей строки, нам нужно переместить курсор влево. Чтобы сделать это, нам нужно дать ему escape-последовательность, которую мы сообщим echo, что будем использовать с -e, а затем переместим курсор, предоставив возвратную каретку \r, которая помещает курсор в начало строки.

$ echo -n "Hello" && echo -e "\rWorld"
World

Может показаться, что это сработало, но посмотрим, что происходит с

$ echo -n "A longer sentance" && echo -e "\rShort sentance"
Short sentancence

Видишь лишних персонажей? Простое написание над строкой только меняет символы, где мы их написали. 

Чтобы исправить это, принятый ответ выше использует escape-символ \e[0K, чтобы стереть все после курсора, после того, как курсор переместился влево. т.е. \r перейти к началу \e[0K стереть до конца.

$ echo -n "A longer sentance" && echo -e "\r\e[0KShort sentance"
Short sentance

Важно\e для запуска escape-последовательностей работает в zsh, но не в sh и не обязательно в bash, однако \033 работает во всех них. Если вы хотите, чтобы ваш скрипт работал в любом месте, вам следует выбрать \033

$ echo -n "A longer sentance" && echo -e "\r\033[0KShort sentance"
Short sentance

Но экранирующие символы могут предоставить еще больше полезности. Например, \033[1A перемещает курсор на предыдущую строку, поэтому нам не нужен -n в предыдущем эхо:

$ echo "A longer sentance" && echo -e "\r\033[1A\033[0KShort sentance"
Short sentance

\r переместиться в начало \033[1A переместиться вверх \033[0K стереть до конца

Наконец, все это немного запутано в моей книге, так что вы можете превратить это в функцию:

overwrite() { echo -e "\r\033[1A\033[[email protected]"; }

Использование [email protected] просто помещает все параметры функции в строку

$ echo Longer sentance && overwrite Short sentence
Short sentence

Надеюсь, что это помогает людям, которые хотят знать немного больше.

8
DanielM

Если вы выводите эхо без символа новой строки echo -n "Something", вы можете использовать \r со своим следующим эхом, чтобы переместить 'курсор' в начало строки echo -e "\\rOverwrite something".

 bash overwrite terminal line

#!/bin/bash

CHECK_MARK="\033[0;32m\xE2\x9C\x94\033[0m"

echo -e "\n\e[4mDoing Things\e[0m"
echo -n "doing thing 1..."
sleep 1
echo -e "\\r${CHECK_MARK} thing 1 done"

Просто знайте, что если ваша новая строка короче вашей старой строки, хвост вашей старой строки все равно будет виден. Обратите внимание на done.. в GIF выше.

5
Derek Soike
#!/bin/bash

echo "Description:"
while test -z $finishInput; do
 read -s tmp
 desc="$desc"$'\n'"$tmp"
 if [ -z "$tmp" ]; then
 finishInput=1
 else
    echo $tmp
 fi
 #echo "fi="$finishInput;
done
echo -n "Maintainer:"
read maintainer

Это решение позволяет избежать пустой строки, но ввод не отражается до завершения линий.

Подсказка: моя версия bash не принимала "[$ finishInput -eq 0]".

1
Michael Besteck

Если вы хотите запустить скрипт в цикле и не взорвать прокрутку, вы можете использовать следующий шаблон:

while sleep 10s; do 
  echo -n $(script)
  echo -n -e "\e[0K\r"
done

Просто замените команду script своей собственной.

0
maxywb