Подтвердить что ты не робот

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

Я заметил, что многие инструменты командной строки, например wget, будут показывать прогресс как число или индикатор выполнения, который продвигается по мере завершения процесса. Хотя вопрос не является специфичным для языка, из языков, которые я чаще всего использую для инструментов командной строки (С++, Node.js, Haskell), я не видел способа сделать это.

Вот пример, три моментальных снимка одиночной строки для терминала, поскольку wget загружает файл: Начало процесса Середина процесса Процесс почти завершен

Наряду с другой информацией, wget показывает индикатор выполнения (< = > ), который продвигается по мере загрузки файла. Объем данных, загруженных до сих пор (6363, 179561, 316053) и текущая скорость загрузки (10,7 КБ/с, 65,8 КБ/с, 63,0 КБ/с) также обновляются. Как это делается?

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

4b9b3361

Ответ 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;
}