在Perl中复制文件时需要显示进度栏

时间:2019-05-15 10:21:29

标签: perl progress-bar cp

我正在将文件从源位置复制到目标位置,并且文件太大。复制文件时,我想在终端窗口中看到进度条。这是Perl代码的示例片段:

$src_dir = "/home/user/source/";
$dest = "/home/user/destination/";

$file = $src_dir."test_file.csv";

`cp $file $dest`;

print "Copy Done\n";

文件将在几秒钟后复制到此处。但是需要在终端窗口中显示进度条。我们如何实现这一目标?

2 个答案:

答案 0 :(得分:1)

以下是如何使用Term::ProgressBar来实现进度条的示例。它使用sysreadsyswrite一次写入固定的块大小:

use strict;
use warnings;
use Term::ProgressBar;
use constant BUFSIZE => 8196;

my $fn = 'file.txt';
my $save_fn = 'file2.txt';
my $size = -s $fn;
my $progress = Term::ProgressBar->new ({count => $size, remove => 1});


open ( my $read_fh, '<:raw', $fn ) or die "Could not open file '$fn': $!";
open ( my $write_fh, '>:raw', $save_fn ) or die "Could not open file '$save_fn': $!";

my $buf = "";

my $total_written = 0;
my $next_update = 0;
while (1) {
    my $bytes_read = sysread $read_fh, $buf, BUFSIZE;
    die "Read error: $!" if !defined $bytes_read;
    last if $bytes_read == 0;
    my $offset = 0;
    my $num_bytes = $bytes_read;
    while (1) {
        my $bytes_written = syswrite $write_fh, $buf, $num_bytes, $offset;
        die "Write error: $!" if !defined $bytes_written;
        die "Unexpected" if $bytes_written > $num_bytes;
        last if $bytes_written == $num_bytes;
        $num_bytes -= $bytes_written;
        $offset += $bytes_written;
    }
    $total_written += $bytes_read;
    if ( $total_written > $next_update ) {
        $next_update = $progress->update($total_written);
    }
}
$progress->update($size);
close $read_fh;
close $write_fh;

答案 1 :(得分:1)

您问了一个令人惊讶的复杂问题。您要运行外部命令(cp),同时让Perl程序继续执行(产生进度信息)。换句话说,它们都在同一时间执行。

经典方法是在一个单独的进程中执行外部命令,并在等待该进程完成的同时输出更新。

第一部分通常是通过fork完成该过程来完成的。容易出错,因此我建议使用Proc::Fork

第二部分需要一些技巧,因为您需要坐在那里等待过程结束,但是需要时不时地输出某些内容,但又不想忙于等待和消耗CPU周期。最安全的方法是在输出之间休眠。为此,我建议使用Time::HiRes(这是标准Perl发行版的一部分)。

还有,这是一些代码:

use strict;
use warnings;

use English; # for more mnemonic special variable names

use POSIX ":sys_wait_h";
use Proc::Fork;
use Time::HiRes qw( usleep );

run_fork {
    child {
        exec q[sleep 3];
    }
    parent {
        my $child_pid = shift;

        my $pid;
        my $loop;
        STDOUT->autoflush;
        while ( 0 == ($pid = waitpid $child_pid, WNOHANG ) ){
            print '.';
            ++$loop;
            usleep(5000);
        }
        print "\n" if $loop;

        # for more complete handlng of $CHILD_ERROR, see the
        # documentation for the perl system function

        die( "error in child process\n" ) if $CHILD_ERROR;

    }
};

如果您想查看进度条,请查看Term::ProgressBar