É possível mostrar a saída do rsync em uma única linha?

2

Eu tenho um script bash que está rsyncing um diretório grande e a função --progress é ótima, mas é possível mostrar toda esta saída em uma única linha? ie. como os arquivos transferem, basta colocar a saída --progress na mesma linha que a anterior, para que eu possa ver o progresso sem a rolagem da tela?

    
por Mark 02.01.2016 / 16:38

2 respostas

3

Script do wrapper

Aqui está um rascunho de um script wrapper, escrito em Perl, que irá emular um PTY (assim o rsync deve se comportar exatamente como faria em um terminal), e analisa a saída para manter uma exibição de duas linhas em execução. o nome do arquivo e o status da transferência. Parece assim:

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

A primeira linha (nome do arquivo, src/test.c ) será alterada dependendo da saída do nome do arquivo atual por rsync . A segunda linha será alterada sempre que rsync gerar uma linha de status atualizada.

NB: optei por um display de 2 linhas (ainda que ainda não rolará!) em vez de um display de 1 linha, pelo menos no meu uso típico, acabo com caminho longo / nomes de arquivos que seriam muito largos quando combinados com a linha de status. No entanto, como você verá abaixo, seria fácil modificar para combinar o nome do arquivo / caminho e o status em uma linha.

Quando rsync sai, o script sai com o mesmo código de saída (para que você ainda possa capturar erros, etc.)

Fundamentação

Com base nas discussões com o OP, as opções rsync incorporadas eram inadequadas, sua versão de rsync é mais antiga e suas necessidades são exclusivas. Assim, senti que um roteiro personalizado era a única maneira de atingir seu objetivo.

Outras opções seriam usar qualquer um dos muitos utilitários de wrapper rsync "backup" existentes já existentes, embora eu não tenha conhecimento de nenhum que suporte saída semelhante.

Código-fonte

 #!/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
 }

Pré-requisitos

O script requer dois módulos não padrão: IPC::Run e IO::Pty . Ambos podem ser instalados com cpan , que vem com o Perl. Muitos, inclusive eu, preferem cpanm , que pode ser instalado com o seguinte one-liner:

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

Então, você executaria:

cpanm IPC::Run IO::Pty

Tipos de terminal suportados

Isso funcionará em praticamente qualquer terminal moderno, já que ele usa movimentos simples de cursor ANSI e códigos de limpeza para sobrescrever continuamente as linhas inferiores da tela.

Uso

O mesmo que rsync em si. Note que você precisa especificar --progress , mas pode facilmente editar em alguns argumentos padrão alterando a linha $h = harness ... :

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

rsync localização binária

O script tenta determinar o local do binário rsync automaticamente com which , que funcionará em quase todos os ambientes. Você também pode editar a linha my $RSYNC='...' para especificar um local personalizado, se desejado ou necessário (importante: altere os backticks (') para aspas simples (') nesse caso.)

Resolução de problemas / extensão

A saída de erro não é tratada especificamente, mas pode ser, com algumas pequenas modificações no script.

Embora razoavelmente robusto, isso é obviamente um esforço "rápido" que não pode explicar todas as saídas possíveis do utilitário rsync incrivelmente complexo. Você pode precisar adaptá-lo para atender às suas necessidades de alguma forma, o que é razoavelmente simples: toda a saída entra na variável $out , que você pode processar de acordo com suas necessidades.

Conversão para exibição de 1 linha em vez de exibição de 2 linhas

Como mencionado acima, optei por uma exibição sem rolagem de 2 linhas para acomodar melhor os caminhos longos. No entanto, converter a saída em uma exibição de 1 linha é trivial. Basta alterar a linha print ... no parse() sub para algo assim:

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

ou, para acabar com os códigos de movimento da ANSI juntos:

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

Então você verá algo assim:

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

Você pode notar que o %-30.30s é uma largura printf arbitrária, e você estaria certo . Você pode empregar algo como a resposta de esta pergunta para obter a largura do terminal para que você possa aumentar / diminuir esse tamanho de acordo.

    
por 02.01.2016 / 20:13
1

Eu tenho um script bash genérico oneline no qual canudo as coisas que quero rolar em uma linha:

#!/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

O tput é para obter os códigos para o retorno de carro e limpar para o final da linha , para que as linhas curtas não deixem lixo das linhas longas anteriores na tela. Note que, se o seu comando estiver saindo para o stderr, você precisará redirecioná-lo para o stdout antes do pipe. Por exemplo, mycommand 2>&1 | oneline .

    
por 02.01.2016 / 17:59