¿Es posible mostrar la salida de rsync en una sola línea?

Tengo un script bash que está rsyncing en un directory grande y la function –progress es genial, pero ¿es posible mostrar todo este resultado en una sola línea? es decir. A medida que se transfieren los files, simplemente escupir el resultado de progreso en la misma línea que el anterior, para que pueda ver el progreso sin que la pantalla se desplace?

Guión Wrapper

Aquí hay un borrador de un script de envoltura, escrito en Perl, que emulará un PTY (para que rsync se comporte exactamente como lo haría en un terminal), y analiza el resultado para que pueda mantener una visualización de dos líneas en ejecución del nombre de file y Estado de transferencia. Se parece a esto:

src/test.c 142 100% 0.19kB/s 0:00:00 (xfr#28, to-chk=0/30) 

La primera línea (nombre de file, src/test.c ) cambiará dependiendo de la salida de nombre de file actual por rsync . La segunda línea cambiará siempre que rsync genere una línea de estado actualizada.

NB: opté por una pantalla de 2 líneas (¡pero que aún no se desplazará!) En lugar de una pantalla de 1 línea, como mínimo en mi uso típico, termino con una ruta larga / nombres de file que serían demasiado amplios cuando combinado con la línea de estado. Sin embargo, como verá a continuación, sería fácil modificar para combinar el file / ruta de acceso y el estado en una línea.

Cuando rsync sale, la secuencia de commands sale con el mismo código de salida (por lo que aún puede atrapar errores, etc.)

Razón fundamental

Según las conversaciones con el OP, las opciones incorporadas de rsync fueron inadecuadas, su versión de rsync es más antigua y sus necesidades son únicas. Por lo tanto, sentí que un guión personalizado era la única forma de lograr su objective.

Otras opciones serían usar cualquiera de las muchas utilidades de envoltura de "copy de security" rsync existentes, aunque no conozco ninguna que soporte resultados similares.

Código fuente

  #!/usr/bin/env perl # Custom progress wrapper for rsync use 5.012; use strict; use warnings; use autodie; use IPC::Run qw/run start pump finish harness/; my $RSYNC=`which rsync`; # Try to get rsync location from PATH chomp $RSYNC; my ($in,$out); # Input and output buffers my $h = harness [ $RSYNC, @ARGV ], '<pty<', \$in, '>pty>', \$out; local $| = 1; # Autoflush output print "\n\n\e[2A\e[s"; # Make room and save cursor position my ($file, $status) = ('',''); # Will hold filename and status lines while ($h->pump) { parse() } parse(); # Don't miss leftover output $h->finish; exit $h->result; # Pass through the exit code from rsync # Parse and display file/status lines from rsync output sub parse { for (split /[\n\r]+/, $out) { $file = $_ if /^\S/; $status = $_ if /^\s/; print "\e[u\e[0J$file\n$status\n"; } $out = ''; # Clear output for next pump } 

Requisitos previos

El script requiere dos modules no estándar: IPC::Run e IO::Pty . Ambos pueden instalarse con cpan , que viene con Perl. Muchos, incluyéndome a mí, prefieren cpanm , que puede instalarse con el siguiente cpanm :

 curl -L https://cpanmin.us | perl - App::cpanminus 

Entonces, correría:

 cpanm IPC::Run IO::Pty 

Tipos de terminales admitidos

Esto funcionará en prácticamente cualquier terminal moderno, ya que utiliza códigos simples de movimiento y borrado de cursor ANSI para sobrescribir continuamente las pocas líneas inferiores de la pantalla.

Uso

Igual que la rsync sí misma. Tenga en count que debe especificar --progress usted mismo, pero podría editar fácilmente en algunos arguments pnetworkingeterminados cambiando la línea $h = harness ...

  my $h = harness [ $RSYNC, '--progress', @ARGV ], '<pty<', \$in, '>pty>', \$out; 

location binaria rsync

La secuencia de commands intenta determinar la location del binary rsync automáticamente con el which , que funcionará en casi todos los entornos. También puede editar la línea my $RSYNC='...' para especificar una location personalizada si así lo desea o es necesario (importante: cambie las barras (`) a comillas simples (') en ese caso).

Solución de problemas / extensión

La salida de error no se maneja específicamente, pero podría ser, con algunas modificaciones menores al script.

Si bien es razonablemente robusto, obviamente se trata de un esfuerzo "rápido" que no puede dar count de todos los resultados posibles de la increíblemente compleja utilidad rsync . Es posible que deba adaptarlo a sus necesidades, lo que es razonablemente sencillo: todos los resultados entran en la variable $out , que puede procesar de acuerdo con sus necesidades.

Conversión a visualización de 1 línea en lugar de visualización de 2 líneas

Como mencioné anteriormente, opté por una pantalla sin desplazamiento de 2 líneas para acomodar mejor nombres de ruta largos. Sin embargo, convertir la salida en una pantalla de 1 línea es trivial. Simplemente cambie la línea de print ... en el sub parse() a algo como esto:

  printf "\e[u\e[0J%-30.30s %s\n", $file, $status; 

o, para eliminar completamente los códigos de movimiento de ANSI:

  printf "\r%-30.30s %-40.40s", $file, $status; STDOUT->flush; # $| = 1 won't help you here 

Entonces verás algo como esto en su lugar:

 src/test.c 142 100% 0.19kB/s 0:00:00 (xfr#28, to-chk=0/30) 

Puede observar que el %-30.30s es un ancho de %-30.30s bastante arbitrario, y estaría en lo cierto. Puede emplear algo así como la respuesta de esta pregunta para get el ancho de la terminal para que pueda crecer / networkingucir ese tamaño en consecuencia.

Tengo una command-line de bash oneline en la que oneline cosas que quiero desplazar en una línea:

 #!/bin/bash cr=`tput cr;tput el` if [ -z "$COLUMNS" ] then COLUMNS=80 fi while read line do echo -n "$cr${line:0:$COLUMNS}" done echo 

El tput es get los códigos para el retorno del carro y borrar al final de la línea para que las líneas cortas no dejen basura de las líneas largas anteriores en la pantalla. Tenga en count que si su command está dando salida a stderr, deberá networkingirigirlo a stdout antes de la tubería. por ejemplo, mycommand 2>&1 | oneline mycommand 2>&1 | oneline