【问题标题】:Why does my jzip process hang when I call it with Perl's system?为什么我用 Perl 的系统调用 jzip 进程会挂起?
【发布时间】:2010-01-07 23:30:11
【问题描述】:

我绝对是 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;

新:

#!/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 指出的那样。

【问题讨论】:

  • jzip 是否要求任何用户反馈?它是否警告不要覆盖现有文件并等待是/否回答?
  • 是否有特定文件失败?如果您尝试打印出 $cmd,它会在停止之前到达哪一个?如果您自己运行该命令会发生什么?它是否执行任何交互操作,例如等待按键或输入?您是否以同一用户身份运行脚本?
  • @John Enabling strict, warnings,声明一个 perl 版本——我希望所有新的 Perl 程序员写的代码都像你的那样“糟糕”!
  • @mopoke:我尝试手动运行命令,它们都运行良好。
  • Perl 的系统、qx 和反引号调用都是同步的——Perl 在继续之前等待函数完成。

标签: perl system


【解决方案1】:

首先,如果您通过 system() 调用使用命令,则应始终将其输出/错误重定向到日志或至少在程序中的进程。

在这种特殊情况下,如果您这样做,您将记录每个命令正在执行的操作,并查看它们是否/何时卡住。

其次,只是一个一般性提示,始终使用本机 Perl 库是个好主意 - 在这种情况下,这当然是不可能的(我对 Windows Perl 没有那么丰富的经验,所以不知道其中是否有 jzip 模块Perl,但搜索 CPAN)。

更新:没有找到 Perl 本机 CAB 提取器,但找到了一个可能更好的 jzip 替代品 - 值得一试。 http://www.cabextract.org.uk/ - 有一个 DOS 版本有望在 Windows 上运行

【讨论】:

  • 感谢您的提示,我绝对同意您的意见,我应该将输出/错误输出到日志中。在这种情况下我这样做了,输出文件不包含任何内容,这意味着它很好。但是,我仍然遇到同样的问题,并且日志没有帮助。关于您的第二点,是的,我也很想使用本机 Perl 模块进行解压缩,但我无法找到任何解压缩 .cab 的库(由 mssql makecab 函数生成)。如果您知道 jzip 以外的任何替代方案,请告诉我。
  • @DVK:谢谢,我发现真正的问题是我需要(以编程方式)在启动另一个进程之前终止正在运行的进程。我试了一下 cabextx,但它的二进制文件似乎不再可用,ibiblio.org/pub/micro/pc-stuff/freedos/files/util/user/…
  • @John - 这很奇怪。您确定问题出在需要杀死(而不是在等待每个人死亡之前并行分叉多个人)吗?此外,我不确定 Windows,但在 Unix 上,您需要在分叉时收集子 PID - 请参阅 Perl Cookbook 了解确切代码。
  • @DVK - 这确实很奇怪。我很了解 Linux,但不知道在 Windows 上发生了什么,我认为发现的问题是真正的原因,因为我看到许多 jzip 实例(甚至记事本,如果我切换到 jzip 到记事本)在运行,即使我杀死了 perl 进程。 Paul Nathan 提出了一些代码来强制脚本一次运行一个进程,但仍然失败。我迷路了。
【解决方案2】:

根据您的编辑,这是我的建议:

#!/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
    }

 }

编辑:添加了异步。 edit2:固定范围问题。

【讨论】:

  • @Paul 谢谢,我运行了你的脚本并且 jzip 运行了两次(成功解压缩了两个 cab 文件)然后再次挂起。我仔细检查了数组,所有命令的格式都正确。检查正在运行的进程,只运行了一个 jzip。但是当我停止脚本时,perl 脚本被杀死并且 jzip 仍然存在。看起来我必须在继续之前杀死它,我该怎么做?
  • 我再次运行了你的脚本,它显示“我的”变量 $fresult 掩盖了同一范围内的早期声明的语法错误
  • @我将此标记为我的解决方案,因为它给了我如何解决问题的灵感。我已经发布了我的修复以及编辑中的真正问题。
【解决方案3】:

从dos窗口运行jzip命令会发生什么?它工作正常吗?如果在脚本中的命令中添加行尾字符 (\n) 会发生什么?这是否可以防止挂起?

【讨论】:

  • @David:从 dos 运行 jzip 时,一切正常。在末尾添加 \n 实际上会启动更多 jzip 实例,但脚本仍然挂起,并且只有一个或有时多个(只是重新运行脚本)cab 文件被解压缩。
  • @John:你有机会使用 Cygwin 吗?
  • @David - 我想他说他使用的是 Windows XP。
  • @Jay:OP 代码的第一行是“#!/usr/bin/perl”,这是我在 Windows 系统上最不希望看到的。我怀疑 OP 可能正在使用 Unix 仿真层,如 Cygwin、MKS 或 SFU。这就是我问这个问题的原因。
  • @David - Jay 说得对,我使用的是 XP,我输入 #!/usr/bin/perl 的原因是为了确保我的脚本可以在 *nix 上运行。对于 ActivePerl,它不需要路径声明,因为它被包装在 env 变量中。
【解决方案4】:

这是一个替代方法,使用 extract.exe,您可以下载 herehere

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;

【讨论】:

  • @ghostdog - 感谢您的指点,用 jzip 替换 extract 确实有效。但这不是问题的真正解决方案(jzip 提供了一个 GUI,但不知道如何关闭。)
【解决方案5】:

尽管没有人明确提及,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::RunIPC::Open3这样的模块。

有时它很糟糕,而这种情况就是其中之一。

【讨论】:

  • @brian - 感谢您的建议。我按照您的想法调用了 system( '/path/to/jzip', '-eo', 'literal_file_name' ); 21次,解压2个文件后挂起。我会在一分钟内研究 IPC::System::Simple。
  • 您能否澄清一下:它是在两次系统调用后挂起,还是在一次系统调用中仅从存档中提取两个文件后挂起?如果我知道这两种方式,我就不能提供帮助。 :)
  • @brian - 我解决了问题并发布了更新。它在一个系统调用后挂起(有时是 2 或 3 个,取决于运气。是的,我知道这很奇怪),每个系统调用都提取一个输入文件。当利用 jzip 时,问题是我包含了使用 XML::DOM 并且在删除它后神奇地工作。我完全迷失了,但它有效。
猜你喜欢
  • 2013-08-18
  • 2014-04-27
  • 1970-01-01
  • 2017-02-23
  • 1970-01-01
  • 1970-01-01
  • 2017-09-12
  • 2011-04-21
  • 1970-01-01
相关资源
最近更新 更多