Сделайте так, чтобы вывод команды отображался в одной строке

Можно ли получить вывод команды, например tar, для записи каждой строки вывода только в одну строку?

Пример использования:

tar -options -f dest source | [insert trickery here]

и вывод будет показывать каждый обрабатываемый файл без перемещения экрана: каждый вывод перезаписывает последний. Можно ли это сделать?


Редактировать: кажется, у нас есть рабочий ответ, но давайте пойдем дальше: как насчет того же, но более 5 строк? Вы видите вывод прокрутки, который не влияет на остальную часть терминала. Думаю, у меня есть ответ, но я хотел бы узнать, что вы, ребята, думаете.


person Chris Watts    schedule 05.01.2012    source источник


Ответы (3)


Замените символы новой строки на возврат каретки.

 tar -options -f dest source | cut -b1-$(tput cols) | sed -u 'i\\o033[2K' | stdbuf -o0 tr '\n' '\r'; echo

Объяснение:

  • cut -b1-$(tput cols): усекает вывод tar, если он длиннее, чем ширина терминала. В зависимости от того, насколько мало вы хотите, чтобы ваш терминал двигался, это не является строго необходимым.

  • sed -u 'i\\o033[2K': вставляет пустую строку в начале каждой строки. Параметр -u для sed переводит его в небуферизованный режим. stdbuf -oL sed 'i\\033[2K' также будет работать одинаково.

  • stdbuf -o0 tr '\n' '\r': использует tr для обмена новыми строками с возвратом каретки. Stdbuf гарантирует, что вывод не будет буферизован; без \n на терминале с линейной буферизацией мы бы не увидели вывода.

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

Для проблемы, которую предлагает ваше редактирование:

x=0; 
echo -e '\e[s'; 
tar -options -f dest source | while read line; do
      echo -en "\e[u" 
      if [ $x gt 0 ]; then echo -en "\e["$x"B"; fi;
      echo -en "\e[2K"
      echo -n $line | cut -b1-$(tput cols);
      let "x = ($x+1)%5";
done; echo;

Не стесняйтесь втиснуть все это в одну строку. Это фактически дает альтернативное решение исходной проблемы:

echo -e '\e[s'; tar -options -f dest source | while read line; do echo -en "\e[u\e2K"; echo -n $line | cut -b1-$(tput cols); done; echo

который точно не зависит ни от чего, кроме кодов VT100.

person Dave    schedule 05.01.2012
comment
Или с возвратом каретки, что приводит к перезаписи следующей строки: tr '\012' '\015' - person tripleee; 05.01.2012
comment
Я думаю, вы хотите напечатать одну новую строку в конце, чтобы, когда она закончилась, она не испортила ваше приглашение. - person mrj; 05.01.2012
comment
Я не думаю, что это то, что ищет ОП вообще. Он хочет какой-то трюк с управляющими символами (с пробелами или чем-то еще), чтобы весь вывод записывался в одно и то же место. - person Dan Fego; 05.01.2012
comment
@DanFego: это то, что это теоретически делает ... за исключением того, что tr не сбрасывает стандартный вывод после записи \r, поэтому он фактически ничего не печатает до конца, а также заменяет последнюю новую строку на возврат каретки, поэтому подсказка перезаписывает последнюю линия вывода. - person mrj; 05.01.2012
comment
Интересно, есть ли какая-то магия setterm, которая сделает стандартный вывод более чистым... - person Dave; 06.01.2012
comment
Тар это не нравится. Он игнорирует канал и принимает остальные параметры как стандартные =[ - person Chris Watts; 07.01.2012
comment
Я не уверен, почему все голосуют за это; это не работает, так как стандартный вывод не сбрасывается. - person mrj; 07.01.2012
comment
+1 Это отлично работает для меня, если ни одна из строк не шире текущей ширины терминала. Убунту 11.04. - person Aaron McDaid; 07.01.2012
comment
@CJxD, это невозможно (он игнорирует канал и принимает остальные параметры как стандартные). Что именно происходит, когда вы пытаетесь это сделать? - person Aaron McDaid; 07.01.2012
comment
.. если подумать, на данный момент это далеко не идеально. «старые» строки остаются позади, а иногда строка представляет собой комбинацию недавних строк и старых строк. Какой код xterm для очистки текущей строки? - person Aaron McDaid; 07.01.2012
comment
@ Дэйв, хорошее использование sed, но я думаю, что это должно быть | cut -b1-$(tput cols) | sed -u 'i\\o033[2K' | tr '\n' '\r'; echo , используя cut для обрезки слишком широких линий. - person Aaron McDaid; 07.01.2012
comment
Чтобы уточнить: есть две разные проблемы с шириной строки. Оба должны быть решены. (1) Нам нужно очистить линии, чтобы короткая линия не смешивалась с более старыми, более длинными линиями. (2) Если линия очень длинная, и шире, чем текущая ширина терминала, нам нужно ее обрезать. Эти две проблемы требуют двух отдельных решений. - person Aaron McDaid; 07.01.2012
comment
/minecraft/mcbackup: line 252: tar -cpvC /backups/TEST/serv -f /backups/TEST/back/120108-af.tar * | cut -b1-80 | sed -u 'i\o033[2K' | stdbuf -o0 tr '\n' '\r'; echo: No such file or directory Кроме того, следующая команда у меня есть status=$?. Я предполагаю, что эта переменная теперь не будет содержать результат команды tar, но, вероятно, команды tr. Это правда? - person Chris Watts; 08.01.2012
comment
Мое использование было: $command" | cut -b1-80 | sed -u 'i\o033[2K' | stdbuf -o0 tr '\n' '\r'; echo" где $command хранит команду tar - person Chris Watts; 08.01.2012
comment
Эта цитата действительно есть в $command"? Вы можете столкнуться с проблемами, если это так. Что происходит в простом тестовом случае? Попробуйте ls / | cut -b1-80 | sed -u 'i\\o033[2K' | stdbuf -o0 tr '\n' '\r'; echo. Не забудьте включить оба обратных слэша в 'i\\o033[2K'. - person Dave; 08.01.2012
comment
Их было два, просто один убежал на выходе: я там опечатался. Похоже, это сработало, поскольку все, что я видел, это «var» — последняя папка, но мне пришлось удалить stdbuf и ее параметры, потому что это не было распознано как команда. - person Chris Watts; 08.01.2012
comment
Хорошо, теперь это работает так: $command | cut -b1-80 | sed -u 'i\\o033[2K' | tr '\n' '\r'; echo НО как мне получить код выхода $command? Это говорит мне, удалось это или нет. - person Chris Watts; 08.01.2012

Спасибо Dave/tripleee за основную механику (замена новой строки на возврат каретки), вот действительно работающая версия:

tar [opts] [args] | perl -e '$| = 1; while (<>) { s/\n/\r/; print; } print "\n"'

Установка $| приводит к тому, что perl автоматически сбрасывается после каждого print, вместо того, чтобы ждать новой строки, а завершающая новая строка предотвращает (частичную) перезапись последней строки вывода, когда команда завершается и bash печатает приглашение. (Это действительно уродливо, если оно частичное, с подсказкой и курсором, за которыми следует остальная часть строки вывода.)

Было бы неплохо сделать это с помощью tr, но я не знаю, как заставить tr (или что-то подобное стандартному) сбрасывать стандартный вывод.

Редактировать: предыдущая версия на самом деле уродлива, так как она не очищает остальную часть строки после того, что было выведено. Это означает, что более короткие строки, следующие за более длинными строками, имеют остаточный текст. Это (по общему признанию, уродливое) исправляет это:

tar [opts] [args] | perl -e '$| = 1; $f = "%-" . `tput cols` . "s\r"; $f =~ s/\n//; while (<>) {s/\n//; printf $f, $_;} print "\n"'

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

person mrj    schedule 05.01.2012
comment
Этот способ тоже не нравится. Труба игнорируется по какой-либо причине. Кроме того, будет ли Perl работать для всех или его нужно предварительно установить? - person Chris Watts; 07.01.2012
comment
Не работает и с cp. cp не жалуется, но и не делает того, что должно. Незер делает предыдущий ответ. знак равно - person Chris Watts; 07.01.2012
comment
@CJxD: я проверил это. Это абсолютно работает для меня на Ubuntu. Возможно, вы работаете на другой платформе, где tar отправляет вывод на стандартный вывод вместо стандартного вывода? В этом случае вам нужно будет добавить 2>&1 перед каналом, чтобы перенаправить stderr на стандартный вывод, чтобы канал его поймал. - person mrj; 07.01.2012
comment
@CJxD: О, я видел ваш комментарий к другому ответу о том, что tar принимает все в качестве аргументов. Вы уверены, что у вас не просто проблема с цитированием в вашей команде tar? Bash интерпретирует командную строку, выполняя свои собственные действия с каналом и передавая аргументы tar; tar не имеет возможности схватить трубу, если вы не убежите от нее, поэтому bash не придает ей особого значения. Я тестировал с помощью этой команды: tar xvf foo.tar.gz | perl ... - person Cascabel; 07.01.2012
comment
@jefromi: я полагаю, это звучит более вероятно, чем платформа, отправляющая нормальный вывод в stderr... - person mrj; 07.01.2012

tar -options -f dest source | cut -b1-$(tput cols) | perl -ne 's/^/\e[2K/; s/\n/\r/; print' ;echo

Пояснения:

  • | cut -b1-$(tput cols) Это сделано для того, чтобы столбцы не были слишком широкими.
  • (В perl -ne) s/^/\e[2K/ Этот код очищает текущую строку, стирая «старые» строки. Это должно быть в начале строки, чтобы гарантировать сохранение последней строки вывода, а также гарантировать, что мы не удаляем строку, пока не будет доступна следующая строка.
  • (В perl -ne) s/\n/\r/ Конечно, здесь можно использовать команду tr. Но как только я начал использовать perl, я остался с ним

PS Чтобы уточнить: есть две разные проблемы с шириной строки. Оба должны быть решены. (1) Нам нужно очистить линии, чтобы короткая линия не смешивалась с более старыми, более длинными линиями. (2) Если линия очень длинная, и шире, чем текущая ширина терминала, нам нужно ее обрезать.

person Aaron McDaid    schedule 07.01.2012
comment
Если бы он был в конце строки, Выхода бы не было; Каждая строка, которую он напечатает, будет немедленно стерта. - person Dave; 07.01.2012
comment
@ Дэйв, я немного обновил работу. Это нормально: это в начале, чтобы ... убедиться, что мы не удаляем строку, пока следующая строка не станет доступной. - person Aaron McDaid; 07.01.2012
comment
@AaronMcDaid это нормально - person Dave; 07.01.2012