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.