Как инструменты командной строки меняют свой результат после вывода?
Я заметил, что многие инструменты командной строки, например wget, будут показывать прогресс как число или индикатор выполнения, который продвигается по мере завершения процесса. Хотя вопрос не является специфичным для языка, из языков, которые я чаще всего использую для инструментов командной строки (С++, Node.js, Haskell), я не видел способа сделать это.
Вот пример, три моментальных снимка одиночной строки для терминала, поскольку wget загружает файл:
![Процесс почти завершен]()
Наряду с другой информацией, wget показывает индикатор выполнения (< = > ), который продвигается по мере загрузки файла. Объем данных, загруженных до сих пор (6363, 179561, 316053) и текущая скорость загрузки (10,7 КБ/с, 65,8 КБ/с, 63,0 КБ/с) также обновляются. Как это делается?
В идеале, пожалуйста, укажите образец кода из одного или нескольких из трех упомянутых выше языков.
Ответы
Ответ 1
Просто напечатайте CR (без новой строки), чтобы перезаписать строку. Вот пример программы в perl:
#!/usr/bin/env perl
$| = 1;
for (1..10) {
print "the count is: $_\r";
sleep(1)
}
Я также отключил буферизацию вывода ($| = 1
), чтобы команда печати сразу же отправила свой вывод в консоль, вместо того, чтобы ее буферизировать.
Пример Haskell:
import System.IO
import Control.Monad
import Control.Concurrent
main = do
hSetBuffering stdout NoBuffering
forM_ [1..10] $ \i -> do
putStr $ "the count is: " ++ show i ++ "\r"
threadDelay 1000000
Ответ 2
Глядя на GNU wget repo на GitHub - progress.c
Кажется, они делают это так же, например, печатают \r
и затем перезаписывают.
/* Print the contents of the buffer as a one-line ASCII "image" so
that it can be overwritten next time. */
static void
display_image (char *buf)
{
bool old = log_set_save_context (false);
logputs (LOG_VERBOSE, "\r");
logputs (LOG_VERBOSE, buf);
log_set_save_context (old);
}
Ответ 3
Я могу говорить только о node.js, но встроенный модуль readline
имеет некоторые очень простые функции обработки экрана, встроенные -в. Например:
var readline = require('readline');
var c = 0;
var intvl = setInterval(function() {
// Clear entirety of current line
readline.clearLine(process.stdout, 0);
readline.cursorTo(process.stdout, 0);
process.stdout.write('Progress: ' + (++c) + '%');
if (c === 100)
clearInterval(intvl);
}, 500);
Существуют также сторонние модули, если вы хотите получить более привлекательный, например multimeter
/meterbox
и blessed
/blessed-contrib
.
В общем говоря, некоторые программы используют ncurses, в то время как другие просто просто вручную выводят escape-коды ANSI, чтобы очистить и перерисовать текущую строку.
Ответ 4
Вероятно, они используют библиотеку ncurses, но на моем Linux
для моих личных инструментов командной строки я просто отправляю '\r'
чтобы переместить курсор обратно в начало строки, чтобы перезаписать его с новой информацией о ходе.
#include <thread>
#include <chrono>
#include <iostream>
int main()
{
for(auto i = 0; i < 100; ++i)
{
std::cout << "\rprogress: " << i << "% " << std::flush;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
std::cout << "\rprogress: DONE " << std::flush;
}