【问题标题】:Need to show the progress bar while copying the file in Perl在 Perl 中复制文件时需要显示进度条
【发布时间】:2019-05-15 10:21:29
【问题描述】:

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

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

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

`cp $file $dest`;

print "Copy Done\n";

文件在几秒钟后被复制到这里。但需要在终端窗口中显示进度条。我们如何才能做到这一点?

【问题讨论】:

  • 您可以通过从 perl 中逐行写入文件来复制文件(不使用 cp),然后您可以使用例如 Term::ProgressBar。但我想你需要先知道(计算)文件中的行数,所以如果不读取文件两次可能就不可能

标签: perl progress-bar cp


【解决方案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;

【讨论】:

  • 谢谢@hakon。但这就像我在文件中进行操作一样,对吗?我不想进入文件,我想要的只是显示进度条,直到脚本到达最后。
  • 为了复制文件,您需要进入其中。我不确定你的意思?
【解决方案2】:

您提出了一个非常复杂的问题。您想运行外部命令 (cp),同时让 Perl 程序继续执行(生成进度信息)。换句话说,它们都是同时执行的。

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

第一部分通常由forking 进程完成。这个很容易出错,所以我建议使用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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-02
    • 2016-02-17
    • 2017-12-21
    • 1970-01-01
    • 1970-01-01
    • 2016-07-30
    • 1970-01-01
    相关资源
    最近更新 更多