Обновить вывод командной строки

Моя программа (которая написана на Perl, хотя я не думаю, что этот вопрос специфичен для Perl) выводит сообщения о состоянии в одной точке программы в форме Progress: x/yy, где x и yy — числа, например: Progress: 4/38.

Я хотел бы «перезаписать» предыдущий вывод, когда печатается новое сообщение о состоянии, чтобы не заполнять экран сообщениями о состоянии. До сих пор я пробовал это:

my $progressString = "Progress\t$counter / " . $total . "\n";
print $progressString;
#do lots of processing, update $counter
my $i = 0;
while ($i < length($progressString)) {
    print "\b";
    ++$i;
}

Символ возврата не будет напечатан, если я включу новую строку в $progressString. Однако, если я пропущу новую строку, выходной буфер никогда не сбрасывается и ничего не печатается.

Какое хорошее решение для этого?


person yavoh    schedule 15.02.2011    source источник


Ответы (5)


Используйте автосброс с STDOUT:

local $| = 1; # Or use IO::Handle; STDOUT->autoflush;

print 'Progress: ';
my $progressString;
while ...
{
  # remove prev progress
  print "\b" x length($progressString) if defined $progressString;
  # do lots of processing, update $counter
  $progressString = "$counter / $total"; # No more newline
  print $progressString; # Will print, because auto-flush is on
  # end of processing
}
print "\n"; # Don't forget the trailing newline
person Michael Goldshteyn    schedule 15.02.2011
comment
Это работает. Я получаю довольно искаженный вывод, например: 5 / 73 / 70 / 7. Во время работы кода я вижу различные искаженные формы слова «Прогресс» и чисел. - person yavoh; 16.02.2011
comment
Я думаю, что что-то в коде обработки связано со стандартным выводом. Сам по себе ваш код работает. Спасибо! - person yavoh; 16.02.2011

Сказать

$| = 1

где-то в начале вашей программы, чтобы включить автоматическую очистку выходного буфера.

Также рассмотрите возможность использования "\r" для перемещения курсора обратно в начало строки, вместо того, чтобы пытаться явно подсчитать, сколько пробелов вам нужно переместить назад.

Как вы сказали, не распечатывайте новую строку, пока работает ваш счетчик прогресса, иначе вы распечатаете свой прогресс в отдельной строке вместо того, чтобы перезаписывать старую строку.

person mob    schedule 15.02.2011

Я знаю, что это не совсем то, что вы просили, но, возможно, лучше. Я столкнулся с той же проблемой, и поэтому вместо того, чтобы слишком много с ней разбираться, я использовал Term::ProgressBar что тоже выглядит красиво.

person Joel Berger    schedule 16.02.2011

Вы также можете использовать экран-коды ANSI для прямого управления курсором. Или вы можете использовать Term::ReadKey, чтобы сделать то же самое. .

person shawnhcorey    schedule 15.02.2011

Сегодня мне пришлось заняться чем-то подобным. Если вы не против перепечатать всю строку, вы можете сделать что-то вроде этого:

print "\n";
while (...) {
     print "\rProgress: $counter / $total";
     # do processing work here
     $counter++;
}
print "\n";

Символ «\r» — это возврат каретки — он возвращает курсор в начало строки. Таким образом, все, что вы распечатываете, перезаписывает текст предыдущего уведомления о ходе выполнения.

person Anthony Agresta    schedule 15.02.2011