Як видалити та замінити останній рядок у терміналі за допомогою bash?


99

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

Кінцевий результат повинен виглядати так:

$ Elapsed time 5 seconds

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

$ Elapsed time 15 seconds

Відповіді:


116

повторити повернення каретки з \ 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 справді заморожує termianl :-)
Налагоджувач

11
Коли ви використовуєте такі речі, як "for i in $ (...)" або "for i in {1..N}", ви генеруєте всі елементи перед ітерацією, що дуже неефективно для великих входів. Ви можете скористатися перевагами каналів: "seq 1 100000 | під час читання i; do ..." або скористатися стилем bash c для циклу: "for ((i = 0 ;; i ++)); do ..."
Tokland

Дякую Дугласу та Токленду - хоча виробництво послідовностей не було безпосередньо частиною питання, яке я змінив на більш ефективний канал
Кен

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
Це дуже добре працює в моєму середовищі. Будь-які знання про сумісність?
Alexander Olsson

21
У Bash принаймні це можна скоротити до \e[Kзамість \033[K.
Dave James Miller

Чудова відповідь! Відмінно працює за допомогою путів з рубіну. Тепер частина мого набору інструментів.
phatmann

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

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

19

Відповідь Дерека Вейта працює добре, якщо довжина рядка ніколи не перевищує ширину терміналу. Якщо це не так, наступний код запобіжить непотрібний вивід:

перед тим, як рядок написано вперше, зробіть

tput sc

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

tput rc
tput ed
echo "your stuff here"

спочатку повернутися до збереженого положення курсору, потім очистити екран від курсору донизу і, нарешті, записати вихідні дані.


Дивний, це нічого не робить у термінаторі. Чи знаєте ви, чи існують обмеження на сумісність?
Джеймі Пейт,

1
Примітка для Cygwin: Вам потрібно встановити пакет "ncurses", щоб використовувати "tput".
Джек Міллер,

Хм ... Здається, це не спрацьовує, якщо на виході більше одного рядка. Це не працює: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

FYI, ось список команд tput " Кольори та рух курсора за допомогою
tput"

12

Метод \ 033 у мене не спрацював. Метод \ r працює, але насправді нічого не стирає, а просто кладе курсор на початок рядка. Отже, якщо новий рядок коротший за старий, ви можете побачити залишки тексту в кінці рядка. Зрештою tput був найкращим способом піти. Він має інші варіанти використання, крім матеріалів курсору, плюс він попередньо встановлений у багатьох дистрибутивах 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"

Це справді очищає весь рядок, проблема для мене занадто
мерехтить

5

Якщо результат виведення багаторядковий, або сценарій вже надрукував символ нового рядка, ви можете перейти до рядків, наприклад:

printf "\033[5A"

що змусить курсор перейти на 5 рядків вгору. Тоді ви можете переписати все, що вам потрібно.

Якщо це не спрацювало, ви можете спробувати printf "\e[5A"або echo -e "\033[5A", що мало б мати той самий ефект.

В основному, за допомогою екранованих послідовностей ви можете контролювати майже все на екрані.


Портативний еквівалент цього 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.