Как удалить и заменить последнюю строку в терминале с помощью bash?


99

Я хочу реализовать индикатор выполнения, показывающий прошедшие секунды в bash. Для этого мне нужно стереть последнюю строку, показанную на экране (команда «очистить» стирает весь экран, но мне нужно стереть только строку индикатора выполнения и заменить ее новой информацией).

Окончательный результат должен выглядеть так:

$ Elapsed time 5 seconds

Затем через 10 секунд я хочу заменить это предложение (в той же позиции на экране) на:

$ Elapsed time 15 seconds

Ответы:


117

повторить возврат каретки с помощью \ r

seq 1 1000000 | while read i; do echo -en "\r$i"; done

от человека эхо:

-n     do not output the trailing newline
-e     enable interpretation of backslash escapes

\r     carriage return

19
for i in {1..100000}; do echo -en "\r$i"; doneчтобы избежать вызова seq :-)
Дуглас Лидер

Это именно то, что мне нужно, спасибо !! И использование команды seq действительно замораживает termianl :-)
Debugger

11
Когда вы используете такие вещи, как «for i in $ (...)» или «for i in {1..N}», вы генерируете все элементы перед повторением, что очень неэффективно для больших входных данных. Вы можете использовать конвейеры: "seq 1 100000 | while read i; do ..." или использовать цикл for в стиле c bash: "for ((i = 0 ;; i ++)); do ..."
tokland 05

Спасибо Дугласу и Токленду - хотя постановка секвенсоров не входила напрямую в вопрос, я перешел на более эффективную трубку Токланда
Кен

1
Мой матричный принтер полностью испортил мою бумагу. Он продолжает забивать точки на том же листе бумаги, которого больше нет, как долго эта программа работает?
Роб

185

Сам по себе возврат каретки перемещает курсор только в начало строки. Это нормально, если каждая новая строка вывода по крайней мере такая же, как и предыдущая, но если новая строка короче, предыдущая строка не будет полностью перезаписана, например:

$ echo -e "abcdefghijklmnopqrstuvwxyz\r0123456789"
0123456789klmnopqrstuvwxyz

Чтобы фактически очистить строку для нового текста, вы можете добавить \033[Kпосле \r:

$ echo -e "abcdefghijklmnopqrstuvwxyz\r\033[K0123456789"
0123456789

http://en.wikipedia.org/wiki/ANSI_escape_code


3
Это действительно хорошо работает в моей среде. Есть какие-нибудь знания о совместимости?
Александр Олссон

21
В Bash по крайней мере, может быть сокращен до \e[Kвместо \033[K.
Дэйв Джеймс Миллер

Отличный ответ! Отлично работает с путами из рубина. Теперь часть моего набора инструментов.
phatmann

@The Pixel Developer: Спасибо за попытку улучшить, но ваше изменение было неправильным. Сначала должен произойти возврат, чтобы переместить курсор в начало строки, затем kill очищает все от этого местоположения курсора до конца, оставляя всю строку пустой, что является намерением. Вы помещаете их в начало каждой новой строки, как и в ответе Кена, так что он записывается на пустой строке.
Дерек Вайт

1
Для записи, можно сделать \033[Gio и \rраньше, \033[Kхотя, очевидно, \rэто намного проще. Также invisible-island.net/xterm/ctlseqs/ctlseqs.html дает больше подробностей, чем Википедия, и от разработчика xterm.
jamadagni

19

Ответ Дерека Вейта работает хорошо, если длина строки никогда не превышает ширину терминала. Если это не так, следующий код предотвратит нежелательный вывод:

перед тем, как строка будет написана впервые, выполните

tput sc

который сохраняет текущую позицию курсора. Теперь, когда вы хотите напечатать свою строку, используйте

tput rc
tput ed
echo "your stuff here"

чтобы сначала вернуться в сохраненную позицию курсора, затем очистить экран от курсора до низа и, наконец, записать результат.


Странно, это ничего не значит для терминатора. Вы знаете, есть ли ограничения совместимости?
Джейми Пейт

1
Примечание для Cygwin: для использования «tput» необходимо установить пакет «ncurses».
Джек Миллер,

Хм ... Кажется, не работает, если на выходе больше одной строки. Это не работает:tput sc # save cursor echo '' > sessions.log.json while [ 1 ]; do curl 'http://localhost/jolokia/read/*:type=Manager,*/activeSessions,maxActiveSessions' >> sessions.log.json echo '' >> sessions.log.json cat sessions.log.json | jq '.' tput rc;tput el # rc = restore cursor, el = erase to end of line sleep 1 done
Nux

@Nux Вам нужно использовать, tput edа не tput el. Использовался пример @Um ed(возможно, он исправил его после того, как вы прокомментировали).
Studgeek

К вашему сведению, вот список команд tput Цвета и движение курсора с tput
studgeek

12

Метод \ 033 у меня не сработал. Метод \ r работает, но на самом деле ничего не стирает, а просто помещает курсор в начало строки. Итак, если новая строка короче старой, вы можете увидеть оставшийся текст в конце строки. В конце концов, путь был лучшим выходом. У него есть и другие варианты использования, помимо курсора, плюс он предустановлен во многих дистрибутивах Linux и BSD, поэтому должен быть доступен для большинства пользователей bash.

#/bin/bash
tput sc # save cursor
printf "Something that I made up for this string"
sleep 1
tput rc;tput el # rc = restore cursor, el = erase to end of line
printf "Another message for testing"
sleep 1
tput rc;tput el
printf "Yet another one"
sleep 1
tput rc;tput el

Вот небольшой сценарий обратного отсчета, с которым можно поиграть:

#!/bin/bash
timeout () {
    tput sc
    time=$1; while [ $time -ge 0 ]; do
        tput rc; tput el
        printf "$2" $time
        ((time--))
        sleep 1
    done
    tput rc; tput ed;
}

timeout 10 "Self-destructing in %s"

Это действительно очищает всю строку, проблема для меня слишком много мерцает: '(
smarber

5

В случае, если вывод выполнения является многострочным или скрипт уже распечатал символ новой строки, вы можете перескакивать на строки с помощью чего-то вроде:

printf "\033[5A"

что заставит курсор прыгнуть на 5 строк вверх. Затем вы можете перезаписать все, что вам нужно.

Если это не сработает, вы можете попробовать printf "\e[5A"или echo -e "\033[5A", что должно иметь тот же эффект.

По сути, с помощью escape-последовательностей вы можете управлять практически всем на экране.


Переносимый эквивалент этого - tput cuu 5где 5 - это количество строк ( cuuдля перемещения вверх, cudдля перемещения вниз).
Маэлан

4

Используйте символ возврата каретки:

echo -e "Foo\rBar" # Will print "Bar"

Это самый простой ответ. Вы даже можете достичь того же эффекта, используя: printf "Foo \ rBar"
lee8oi

1

Этого можно добиться, поместив возврат каретки \r.

В одной строке кода с printf

for i in {10..1}; do printf "Counting down: $i\r" && sleep 1; done

или с echo -ne

for i in {10..1}; do echo -ne "Counting down: $i\r" && sleep 1; done
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.