我绝对是Perl的新手,请原谅我,如果这对你来说似乎是一个愚蠢的问题。
我试图在Perl(ActivePerl,jzip,Windows XP)中使用jzip解压缩一堆 .cab 文件:
#!/usr/bin/perl
use strict;
use warnings;
use File::Find;
use IO::File;
use v5.10;
my $prefix = 'myfileprefix';
my $dir = '.';
File::Find::find(
sub {
my $file = $_;
return if -d $file;
return if $file !~ /^$prefix(.*)\.cab$/;
my $cmd = 'jzip -eo '.$file;
system($cmd);
}, $dir
);
代码解压缩文件夹中的第一个 .cab 文件并挂起(没有任何错误)。它挂在那里直到我按Ctrl + c停止。谁知道问题是什么?
编辑:我使用了processxp来检查进程,我发现启动了正确数量的jzip进程(每个cab文件驻留在源文件夹中)。但是,只有其中一个在cmd.exe =>下运行。 perl,这些进程在被解雇后都没有关闭。在我看来,我需要关闭进程并逐个执行,我不知道如何在perl中执行此操作。有什么指针吗?编辑:我也尝试用记事本替换jzip,事实证明它一次打开记事本一个文件(按顺序),并且只有当我手动关闭记事本然后触发另一个实例。这是ActivePerl中的常见行为吗?
编辑:我终于解决了,我仍然不能完全确定原因。我所做的是删除脚本中的XML库,这应该是不相关的。抱歉,我开始有目的地删除了“使用XML :: DOM”,因为我认为这与此问题完全无关。 旧: 用严格; 使用警告;use File::Find;
use IO::File;
use File::Copy;
use XML::DOM;
use DBI;
use v5.10;
NEW:
#!/usr/bin/perl
use strict;
use warnings;
use File::Find;
use IO::File;
use File::Copy;
use DBI;
use v5.10;
my $prefix = 'myfileprefix';
my $dir = '.';
# retrieve xml file within given folder
File::Find::find(
sub {
my $file = $_;
return if -d $file;
return if $file !~ /^$prefix(.*)\.cab$/;
say $file;
#say $file or die $!;
my $cmd = 'jzip -eo '.$file;
say $cmd;
system($cmd);
}, $dir
);
然而,这会产生另一个问题,当提取的文件已经存在时,脚本将再次挂起。我非常怀疑这是jzip的问题,解决问题的另一种方法就是用提取替换jzip,就像下面指出的@ ghostdog74一样。
答案 0 :(得分:1)
首先,如果您通过system()调用使用命令,则应始终将其输出/错误重定向到日志或至少在程序中处理。
在这种特殊情况下,如果你这样做,你就会记录每一个命令正在做什么,并会看到是否/何时它们被卡住了。
其次,只是一般提示,总是使用本机Perl库是个好主意 - 在这种情况下,当然可能是不可能的(我不熟悉Windows Perl,所以没有线索,如果有一个jzip模块Perl,但搜索CPAN)。
UPDATE :没有找到Perl原生CAB提取器,但找到了一个可能更好的jzip替换 - 值得一试。 http://www.cabextract.org.uk/ - 有一个DOS版本可以在Windows上运行
答案 1 :(得分:0)
根据您的编辑,我建议这样做:
#!/usr/bin/perl
use strict;
use warnings;
use File::Find;
use IO::File;
use v5.10;
my $prefix = 'myfileprefix';
my $dir = '.';
my @commands;
File::Find::find(
sub {
my $file = $_;
return if -d $file;
return if $file !~ /^$prefix(.*)\.cab$/;
my $cmd = "jzip -eo $File::Find::name";
push @commands, $cmd;
}, $dir
);
#asynchronously kick off jzips
my $fresult;
for @commands
{
$fresult = fork();
if($fresult == 0) #child
{
`$_`;
}
elsif(! defined($fresult))
{
die("Fork failed");
}
else
{
#no-op, just keep moving
}
}
编辑:添加了asynch。 edit2:固定范围问题。
答案 2 :(得分:0)
从dos窗口运行jzip命令会发生什么?它运作正常吗?如果在脚本中的命令中添加行尾字符(\ n),会发生什么?这会阻止挂起吗?
答案 3 :(得分:0)
这是另一种方法,使用extract.exe,您可以下载here或here
use File::Find;
use IO::File;
use v5.10;
my $prefix = 'myfileprefix';
my $dir = '.';
File::Find::find({wanted => \&wanted}, '.');
exit;
sub wanted {
my $destination = q(c:\test\temp);
if ( -f $_ && $_=~/^$prefix(.*)\.cab$/ ) {
$filename = "$File::Find::name";
$path = "$File::Find::dir";
$cmd = "extract /Y $path\\$filename /E /L $destination";
print $cmd."\n";
system($cmd);
}
} $dir;
答案 4 :(得分:0)
虽然没有人明确提到过,system会阻止该进程完成。正如人们所指出的那样,真正的问题是弄清楚为什么这个过程不会退出。分叉或任何其他并行化将无济于事,因为您将留下许多挂起的进程。
直到你能解决问题,从小处开始。制作用于演示问题的最小Perl脚本:
#!perl
system( '/path/to/jzip', '-eo', 'literal_file_name' ); # full path, list syntax!
print "I finished!\n";
现在的诀窍是找出它挂起的原因,有时这意味着不同的外部程序的解决方案。有时您需要在运行外部进程之前关闭STDIN,或者它在那里等待它关闭,有时您还会做其他事情。
除了system
之外,您还可以尝试使用IPC::System::Simple来处理大量特定于平台的详细信息,或者IPC::Run或IPC::Open3等模块。
有时它很糟糕,这种情况就是其中之一。