Net::OpenSSH; rsync_put 将进度写入文件,scp_put 不会

Net::OpenSSH; rsync_put writes progress into file, scp_put doesn't

rysnc_putscp_put 都在终端中显示进度。 但是当我尝试将其写入文件时,我只能看到 rysnc_put 的进度。即使我只是打印 pty [我的编辑] 的输出,问题仍然存在。 这是我 pty 用法的问题吗? 仅针对 scp_put 提到,当 STDOUT 不是 tty 时没有进展。

Note that scp will not generate progress reports unless its stdout stream is attached to a tty.

#!/usr/bin/perl
use strict;
use warnings;
use Net::OpenSSH;

my $SSH = Net::OpenSSH->new(
    '192.168.178.33',
    (
        user => 'TT',
        password => 'TTpassword',
        port => '22',
    )
);

my $pty = new IO::Pty;
my $slave = $pty->slave;

$SSH->scp_put(
    {
        quiet => 0,
        verbose => 1,
        stdout_fh => $pty,
        stderr_fh => $pty,
        bwlimit => '200',
    },
    '/opt/TT/CHANGES.md',
    '/opt/TT/CHANGES.md',,
);

while (<$pty>) {
    print STDOUT "$_";
}

即使 scp_putrsync_put 是相似的方法,它们包装了两个不相关的程序(scprsync),就此而言,它们不会在同理。

具体来说,scp 检查它的 stdio 流是否附加到 pty,如果没有,它会抑制进度消息。据我所知,该功能无法禁用。

更新:并行读取 PTY:

此代码从 PTY 读取数据并将数据并行写入文件:

use strict;
use warnings;

use Net::OpenSSH;
use POSIX ();
use IO::Pty;

my $ssh = Net::OpenSSH->new("localhost"); #, scp_cmd => "/home/salva/t/openssh-8.9p1/scp");

my $pty = new IO::Pty;
my $slave = $pty->slave;
print "slave: $slave, fd: ".fileno($slave)."\n";

my $logger_pid = fork;
unless ($logger_pid) {
  defined $logger_pid or die "fork failed: $!";

  open my $log, ">", "/tmp/scp.log";
  select $log;
  $| = 1;

  print("Reading from PTY\n");
  while (1) {
      my $buffer;
      sysread($pty, $buffer, 1);
      syswrite($log, $buffer, 1);
  }
  print "PTY closed unexpectedly\n";
  POSIX::_exit();
}


my $scp_pid = fork;
unless($scp_pid) {
    defined $scp_pid or die "fork failed: $!";

    $pty->make_slave_controlling_terminal();
    $ssh->scp_put({stdout_fh => $slave,
                   stderr_fh => $slave,
                   quiet => 0},
                  "/etc/passwd", "/tmp/foo.vdi");
}

waitpid($scp_pid, 0);

kill 9, $logger_pid;
waitpid($logger_pid, 0);

您的问题的另一种解决方案是自定义可从 Net::SSH::Any 获得的 SCP 协议的纯 perl 实现。

我从那个包中设计了 SCP 类 进行子类化,这样就可以完成类似的事情,但从来没有真正记录 API,所以,好吧,你会依赖未记录的功能(不过,我不打算更改它)。

use strict;
use warnings;

package MyPutter {
    use parent 'Net::SSH::Any::SCP::Putter::Standard';

    use Data::Dumper qw(Dumper);

    for my $name (qw(open_dir open_file close_file close_dir)) {
        my $method = $name;
        my $super = Net::SSH::Any::SCP::Putter::Standard->can($name);
        no strict 'refs';
        *$name = sub {
            my ($putter, $a) = @_;
            print STDERR "$method $a->{path} --> $a->{local_path}\n";
            $super->(@_);
        };
    }

    sub read_file {
        my $putter = shift;
        my $a = shift;
        my $data = $putter->SUPER::read_file($a, @_);
        $a->{bytes_copied} += length($data);
        print STDERR "read_file $a->{path} --> $a->{local_path} $a->{bytes_copied}/$a->{size}\n";
        return $data;
    }
};

use Net::OpenSSH;
my $ssh = Net::OpenSSH->new("localhost");

my $any = $ssh->any;
my $putter = MyPutter->_new($any, {}, "/tmp/out2", "/tmp/passwd3");
$putter->run({})