我有一个正在同步大目录的 bash 脚本,并且 --progress 函数很棒,但是是否可以在一行上显示所有这些输出? IE。当文件传输时,只需将 --progress 输出吐到与最后一行相同的行上,这样我就可以在不滚动屏幕的情况下观看进度?
答案1
包装脚本
这是用 Perl 编写的包装器脚本的草稿,它将模拟 PTY(因此 rsync 的行为应该与终端中的行为完全相同),并解析输出,以便它可以保持文件名的运行两行显示和转移状态。它看起来像这样:
src/test.c
142 100% 0.19kB/s 0:00:00 (xfr#28, to-chk=0/30)
第一行 (filename, src/test.c
) 将根据 输出的当前文件名而变化rsync
。每当rsync
输出更新的状态行时,第二行就会更改。
注意:我选择了 2 行显示(但仍然不会滚动!)而不是 1 行显示,因为至少在我的典型用法中,我最终会得到长路径/文件名,当与状态行。但是,正如您将在下面看到的,将文件/路径名和状态合并到一行中很容易进行修改。
退出时rsync
,脚本以相同的退出代码退出(因此您仍然可以捕获错误等)
基本原理
根据与 OP 的讨论,内置rsync
选项不足,他们的版本rsync
较旧,并且他们的需求是独特的。因此,我认为自定义脚本是实现他们目标的唯一方法。
其他选项是使用现有的许多rsync
“备份”包装实用程序中的任何一个,尽管我不知道有任何支持类似输出的实用程序。
源代码
#!/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
}
先决条件
该脚本需要两个非标准模块:IPC::Run
、 和IO::Pty
。这两个都可以cpan
通过 Perl 附带的 来安装。许多人,包括我在内,更喜欢cpanm
,它可以通过以下一行安装:
curl -L https://cpanmin.us | perl - App::cpanminus
然后,你会运行:
cpanm IPC::Run IO::Pty
支持的终端类型
这将在实践中发挥作用任何现代终端,因为它使用简单的 ANSI 光标移动和清除代码来不断覆盖屏幕的底部几行。
用法
和自己一样rsync
。请注意,您需要指定--progress
自己,但您可以通过更改行轻松编辑某些默认参数$h = harness ...
:
my $h = harness [ $RSYNC, '--progress', @ARGV ], '<pty<', \$in, '>pty>', \$out;
rsync
二进制位置
rsync
该脚本尝试使用 自动确定二进制文件的位置which
,这几乎适用于所有环境。my $RSYNC='...'
如果需要,您还可以编辑该行以指定自定义位置(重要的:在这种情况下,将反引号 (`) 更改为单引号 (')。)
故障排除/扩展
错误输出没有被专门处理,但可以通过对脚本进行一些小的修改来处理。
虽然相当稳健,但这显然是一个“快速”的工作,无法解释这个极其复杂的rsync
实用程序的所有可能输出。您可能需要对其进行一些调整以适应您的需求,这希望相当简单:所有输出都进入变量$out
,您可以根据您的需求进行处理。
转换为 1 行显示而不是 2 行显示
如上所述,我选择了 2 线非滚动显示以更好地适应长路径名。然而,将输出转换为 1 行显示是很简单的。只需将子print ...
中的行更改parse()
为如下所示:
printf "\e[u\e[0J%-30.30s %s\n", $file, $status;
或者,完全废除 ANSI 运动代码:
printf "\r%-30.30s %-40.40s", $file, $status;
STDOUT->flush; # $| = 1 won't help you here
然后你会看到类似这样的东西:
src/test.c 142 100% 0.19kB/s 0:00:00 (xfr#28, to-chk=0/30)
您可能会注意到这%-30.30s
是一个相当任意的打印函数宽度,你是对的。您可以采用类似答案的内容这个问题获取终端宽度,以便您可以相应地增大/缩小该尺寸。
答案2
我有一个通用的 bash 脚本oneline
,我将想要滚动到一行的内容通过管道传输到其中:
#!/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
这tput
是为了获取回车符和清除到行尾因此短行不会在屏幕上留下以前长行的垃圾。请注意,如果您的命令输出到 stderr,您需要将其重定向到管道之前的 stdout。例如mycommand 2>&1 | oneline
。