Накарайте изхода на командата да се показва на един ред [затворено]

Възможно ли е да получите изхода на команда - например 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 не изтрива stdout след запис на \r, така че всъщност не отпечатва нищо до края и също така замества последния нов ред с връщане на каретка, така че подканата презаписва последния линия на изхода. - person mrj; 05.01.2012
comment
Чудя се дали има някаква setterm магия, която ще направи stdout по-пласт... - person Dave; 06.01.2012
comment
Тар не харесва това. Той игнорира канала и приема останалите като стандартни параметри =[ - person Chris Watts; 07.01.2012
comment
Не съм сигурен защо всички гласуват за това; не работи, тъй като stdout не се промива. - person mrj; 07.01.2012
comment
+1 Това работи перфектно за мен, ако никоя от линиите не е по-широка от текущата ширина на терминала. Ubuntu 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 (или нещо подобно стандартно) да изчисти stdout.

Редактиране: Предишната версия всъщност е грозна, тъй като не изчиства останалата част от реда след това, което е изведено. Това означава, че по-късите редове след по-дългите имат остатъчен завършващ текст. Това (наистина грозно) коригира това:

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

(Можете също да получите ширината на терминала по повече perl-y начини, както е описано тук; Все пак не исках да завися от CPAN модули.

person mrj    schedule 05.01.2012
comment
Този метод също не харесва. Тръбата се игнорира по някаква причина. Освен това Perl ще работи ли за всички или трябва първо да се инсталира? - person Chris Watts; 07.01.2012
comment
Не работи и с cp. cp не се оплаква, но и не прави каквото трябва. Nether прави предишния отговор. =[ - person Chris Watts; 07.01.2012
comment
@CJxD: Тествах това. Абсолютно работи за мен на Ubuntu. Възможно ли е да сте на друга платформа, където tar изпраща своя изход към stderr вместо stdout? В такъв случай ще трябва да добавите 2>&1 преди канала, за да пренасочите stderr към stdout, така че каналът да го хване. - 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
@Dave, актуализирах малко работата. Добре ли е това: В началото е, за да .. гарантираме, че няма да изтрием ред, докато следващият ред не е наличен. - person Aaron McDaid; 07.01.2012
comment
@AaronMcDaid това е добре - person Dave; 07.01.2012