Необходимо показать индикатор выполнения при копировании файла в Perl - PullRequest
1 голос
/ 15 мая 2019

Я копирую файл из исходного местоположения в место назначения, а размер файла слишком велик.Я хочу видеть индикатор выполнения в окне терминала, когда файл копируется.Вот пример фрагмента кода Perl:

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

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

`cp $file $dest`;

print "Copy Done\n";

Файл копируется сюда через определенные секунды.Но нужно отобразить индикатор выполнения в окне терминала.Как мы можем этого добиться?

Ответы [ 2 ]

1 голос
/ 15 мая 2019

Вы задали удивительно сложный вопрос. Вы хотите запустить внешнюю команду (cp), в то же время продолжая выполнение программы Perl (производя информацию о ходе выполнения). Другими словами, они оба выполняются одновременно.

Классическим способом сделать это является выполнение внешней команды в отдельном процессе и вывод обновлений в ожидании завершения этого процесса.

Первая часть обычно выполняется fork процессом. Это легко понять неправильно, поэтому я предлагаю использовать Proc :: Fork .

Вторая часть требует некоторой утонченности, так как вам нужно сидеть сложа руки и ждать окончания процесса, но время от времени нужно что-то выводить, но не нужно заняты - ждать и записывать циклы процессора. Самый безопасный способ сделать это - это спать между выходами. Для этого я рекомендую 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

1 голос
/ 15 мая 2019

Вот пример того, как вы можете реализовать индикатор выполнения, используя Term :: ProgressBar . Он использует sysread и syswrite для записи фиксированного размера блока за раз:

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;
...