Итак, когда wget
вы получаете веб-страницу, она показывает вам строку состояния, которая показывает, насколько загружен файл(ы). Выглядит это примерно так:
25%[=============>______________________________________] 25,000 100.0K/s
(подчеркивания — это пробелы; я просто не могу понять, как поставить здесь больше одного пробела подряд)
Однако вместо того, чтобы записать еще одну строку в stdout и добавить еще один индикатор выполнения, он обновляет его, вот так:
50%[===========================>________________________] 50,000 100.0K/s
И wget
это не единственный пример. Например, когда вы что-то передаёте по конвейеру, less
а затем выходите, ваш исходный запрос остаётся там, вместе с результатом любых команд, которые вы запускали ранее. Как будто вы никуда и не уходили.
Итак, у меня такие вопросы: как это называется, как это реализовать, работает ли это только для одной строки за раз и можно ли использовать это в C?
решение1
Прежде всего, ваш вопрос не имеет ничего общего с bash, а с терминалом. Терминал отвечает за отображение текста программ, а сам bash не контролирует программы после их запуска.
Терминалы предлагают последовательности управления для управления цветом, шрифтом, положением курсора и т. д. Список стандартизированных последовательностей терминалов см. на http://www.termsys.demon.co.uk/vtansi.htmВы можете, например,
- установите курсор в начале строки
- удалите строку после этого
- написать новую строку
для создания индикатора выполнения.
Более сложные управляющие последовательности терминала обычно зависят от терминала, например, работают только с Eterm или xterm.ncurses- это программная библиотека, которая позволяет создавать интерактивные программы с терминалом, чтобы вам не пришлось использовать escape-последовательности.
Как перезаписать существующую строку терминальными последовательностями
echo long text
sleep 1
printf "\033[1A" # move cursor one line up
printf "\033[K" # delete till end of line
echo foo
Как перезаписать существующую строку без терминальной последовательности
Одним из простых решений является не писать новую строку в конце, а написать возврат каретки, который по сути сбрасывает курсор в начало строки, например:
echo -n first
sleep 1
echo -ne "\rsecond"
echo
Клавиша \r
возврата каретки или курсора переместится в начало строки и позволит вам перезаписать содержимое строки.
Переключайтесь между буферами типа less
илиvi
Поведение less
также обусловлено более продвинутой функцией терминала — альтернативным экраном:
В режиме VT102 имеются escape-последовательности для активации и деактивации альтернативного экранного буфера, который имеет тот же размер, что и область отображения окна. При активации текущий экран сохраняется и заменяется альтернативным экраном. Сохранение строк, прокрученных за пределы верхней части окна, отключено до восстановления обычного экрана. Запись term-cap(5) для xterm позволяет визуальному редактору vi(1) переключаться на альтернативный экран для редактирования и восстанавливать экран при выходе. Всплывающее меню упрощает переключение между обычным и альтернативным экранами для вырезания и вставки.
http://rosettacode.org/wiki/Terminal_control/Preserve_screenперечисляет некоторые примеры того, как сделать это самостоятельно, либо черезtputили с помощью некоторых последовательности действий.
решение2
Вместо использования echo
, который автоматически добавляет новую строку к строке, используйте printf "%s\r" whatever
-- возврат каретки, который отправляет курсор в начало текущей строки. Пример:
seq 1 15 | while read num; do printf "%2d\r" $num; sleep 1; done; echo ""
решение3
Я бы также рекомендовал тем, кто нашел эту тему, взглянуть наBash Prompt HOWTO - Перемещение курсора.
Примеры:
- Position the Cursor:
\033[<L>;<C>H
Or
\033[<L>;<C>f
puts the cursor at line L and column C.
- Move the cursor up N lines:
\033[<N>A
- Move the cursor down N lines:
\033[<N>B
- Move the cursor forward N columns:
\033[<N>C
- Move the cursor backward N columns:
\033[<N>D
- Clear the screen, move to (0,0):
\033[2J
- Erase to end of line:
\033[K
- Save cursor position:
\033[s
- Restore cursor position:
\033[u
Несколько примеров на языке C:
void saveCursorPosition() {
printf("\033[s");
}
void restoreCursorPosition() {
printf("\033[u");
}
void lineUP(short int times) {
printf("\033[%iA", times);
}
void lineDown(short int times) {
printf("\033[%iB", times);
}
Пример программы:
#include <stdio.h>
#include <unistd.h>
void saveCursorPosition() {
printf("\033[s");
}
void restoreCursorPosition() {
printf("\033[u");
}
void lineUP(short int times) {
printf("\033[%iA", times);
}
void moveCursorBackwards(short int times) {
printf("\033[%iD", times);
}
void printMainText() {
printf("\n ╔═══════════════════════════════╗");
printf("\n ║ ║");
printf("\n ║ Progress Bar ║");
printf("\n ║ ║");
printf("\n ║ [] ║");
printf("\n ║ ║");
printf("\n ║ Press Ctrl+C to close ║");
printf("\n ║ ║");
printf("\n ╚═══════════════════════════════╝\n");
}
int main(int argc, char **argv) {
printMainText();
for (int progress=0; progress <= 10; progress++) {
saveCursorPosition();
lineUP(5);
printf("\r ║ [");
fflush(stdout);
for (int i=0; i<progress; i++) {
printf("=>]");
fflush(stdout);
moveCursorBackwards(2);
}
moveCursorBackwards(progress + 5);
printf("%i%%", progress * 10);
fflush(stdout);
restoreCursorPosition();
sleep(1);
}
return 0;
}