在Windows和Linux下的Perl中调用外部程序并实时打印正在执行的操作

时间:2019-01-28 10:38:08

标签: perl

我正在尝试从Perl调用外部程序,即unrar,并允许在运行时将其输出打印到屏幕上。

@lin1=`cd "$wholepath_dir" & $laufwerk & unrar x -v -y "$_" ` if ($^O =~ /Win/) ;       

@lin1=`cd "$wholepath_dir"; unrar x -v -y "$_" ` if (not ($^O =~ /Win/)) ;

此代码在目录内解压缩some-rar-file.rar。 但是,它没有显示任何进展。 如果它能实时显示unrar输出,而又正在进行unraring,而不是将unrar结果提供给数组,那就很好了。

对Windows和Linux都知道如何做到这一点?

4 个答案:

答案 0 :(得分:1)

只显示进度然后忘记它,除了任何错误,对我来说终于没问题了,我通过以下方式解决了它,但仅适用于 Windows :

PreferredSize(
            preferredSize: Size.fromHeight(72),
            child: AppBar(
              title: Text(title, style: TextStyles.h1),
              centerTitle: false,
              elevation: 0,
              brightness: Brightness.light,
              backwardsCompatibility: false,
              bottom: bottomAppBarWidget,
            ))

这会打开一个子shell,因此会弹出另一个cmd.exe,显示整个解压缩过程,如果运行正常则立即关闭或再暂停3秒,如果出现问题,则将错误消息写入“锁定文件”。 稍后检查锁定文件的大小,以便主程序知道是否发生了错误。然后,锁文件被删除。

我想 Linux 没有这样的解决方案吗?

答案 1 :(得分:0)

这应该做到这一点:它可以让Shell处理STDERR的重定向。我没有Windows,所以不知道cmd.exe中的相应位。

#!/usr/bin/perl
use strict;
use warnings;

sub run_command(\@$) {
    my($log, $cmd) = @_;

    my($redirect_start, $redirect_end);
    if ($^O =~ /Win/) {
        # @TODO
        $redirect_start = '???';
        $redirect_end   = '???';
    } else {
        $redirect_start = '(';
        $redirect_end   = ') 2>&1';
    }
    open(my $fh, "${redirect_start}${cmd}${redirect_end} |")
        or die "ERROR: $!\n";

    while (<$fh>) {
        push(@{ $log }, $_);
        print;
    }

    close($fh)
        or die "ERROR: $!\n";
}

my @log;
run_command(@log, 'ls dummy.*');
run_command(@log, 'ls -lht dummy.txt');

print "\nLOG:\n";
print foreach (@log);

exit 0;

我的机器的输出示例:

$ perl dummy.pl
dummy.c
dummy.c~
...
dummy.txt
dummy.txt~
dummy.xml
-rw-rw-r--. 1 USER USER 926 Jan 25 23:33 dummy.txt

LOG:
dummy.c
dummy.c~
...
dummy.txt
dummy.txt~
dummy.xml
-rw-rw-r--. 1 USER USER 926 Jan 25 23:33 dummy.txt

如果上述解决方案不能令人满意,我建议您调查IPC::Open3

答案 2 :(得分:0)

对于像这样的交互式内容,IPC::Run通常是个不错的选择。

use warnings;
use strict;
use IPC::Run qw/ run new_chunker /;

my @cmd = ('yourcommand', 'arg1', 'arg2');
my (@out,@err);
run \@cmd, '>', new_chunker("\n"), sub {
        my $line = shift;
        print $line;
        push @out, $line;
    }, '2>', new_chunker("\n"), sub {
        my $line = shift;
        print STDERR $line;
        push @err, $line;
    } or die $?;

是根据我的帖子here复制的。我还写了关于调用外部命令的主题,其示例代码的长度为here

答案 3 :(得分:0)

首先,请注意,用引号引起来的内容不是保护它免受外壳解析的有效方法,因为它本身可能包含引号。最好的方法是通过使用open / system / open3 / etc的列表形式来完全避免shell解析,但是由于使用的是shell元字符&,因此无法执行此操作。因此,您可以使用String::ShellQuote为适当的外壳提供一个用引号引起来的字符串。

使用IPC::Open3

use strict;
use warnings;
use IPC::Open3;
use String::ShellQuote;

my $quoted_dir = shell_quote $wholepath_dir;
my $quoted_file = shell_quote $_;
my $cmd = $^O =~ /Win/
  ? "cd $quoted_dir & $laufwerk & unrar x -v -y $quoted_file"
  : "cd $quoted_dir; unrar x -v -y $quoted_file";

my $pid = open3 undef, my $stdout, '>&STDERR', $cmd;
my @lines;
while (my $line = readline $stdout) {
  print $line;
  push @lines, $line;
}
waitpid $pid, 0;
my $exit_code = $? >> 8;
# you can now act on the $exit_code and the @lines of STDOUT