【问题标题】:Showing progress whilst running a system() command in Perl在 Perl 中运行 system() 命令时显示进度
【发布时间】:2010-07-07 08:29:53
【问题描述】:

我有一个执行某些任务的 Perl 脚本,其中之一是调用 system 命令到 "tar -cvf file.tar....."

这通常需要一些时间,因此我希望命令行回显进度指示器,例如在 system 调用正在进行时回显到屏幕上的 #

我一直在做一些挖掘工作,偶然发现了fork。这是最好的方法吗?是否可以分叉 system 命令,然后创建一个 while 循环来检查分叉返回的 $pid 的状态?

我还看到了对 waitpid.. 的引用。我猜我也需要使用它。

fork system("tar ... ")
while ( forked process is still active) {
    print #
    sleep 1
}

我是不是找错树了?

非常感谢 约翰

【问题讨论】:

  • fork的返回值是子进程ID,如果是父进程,如果是子进程,则为0,如果fork失败,则为undef。请参阅 perldoc -f fork。

标签: perl


【解决方案1】:

Perl 对此有一个很好的结构,称为“管道打开”。您可以通过在 shell 提示符下键入 perldoc -f open 来了解更多信息。

# Note the use of a list for passing the command. This avoids
# having to worry about shell quoting and related errors.
open(my $tar, '-|', 'tar', 'zxvf', 'test.tar.gz', '-C', 'wherever') or die ...;

这是一个显示示例的 sn-p:

  open(my $tar, '-|', 'tar', ...) or die "Could not run tar ... - $!";
  while (<$tar>) {
       print ".";
  }
  print "\n";
  close($tar);

print "." 替换为每 10 到 100 行左右打印一个井号的东西,以获得漂亮的仪表条。

【讨论】:

  • 嗨,约翰……这正是我所追求的!非常感谢您的回复和示例。看到示例代码时,我总是发现它更容易。问候约翰
  • 逗号不需要,我从示例中删除了它;-)
  • :-) - 谢谢约翰。一件事....这种方法是否仅适用于向 STDOUT 写入内容的命令?使用 tar zcvf 时它似乎工作得很好,但是我正在使用另一个命令,它不会将任何内容回显到 STDOUT .....当我尝试打开该命令的管道时,我看不到打印“ 。” :(
  • @john 是的,它仅在命令写入内容时才有效。如果子进程没有写任何东西,那么你需要使用不同的构造,比如fork/exec/nonblocking waitpid/sleep。 @Johan 您的代码示例使用$|++ 会更好,以便立即打印点。 :)
【解决方案2】:

一个不依赖于子进程写入任何类型输出的示例,只要它正在运行,它就会大约每秒打印一个点:

use POSIX qw(:sys_wait_h);
$|++;

defined(my $pid = fork) or die "Couldn't fork: $!";

if (!$pid) { # Child
  exec('long_running_command', @args) 
    or die "Couldn't exec: $!";
} else { # Parent
  while (! waitpid($pid, WNOHANG)) {
    print ".";
    sleep 1;
  }
  print "\n";
}

虽然它可能会进行更多的错误检查,并且可能实际上已经在 CPAN 上有更好的东西。 Proc::Background 似乎很有希望将这种工作抽象出来,但我不确定它有多可靠。

【讨论】:

  • 您好,感谢您在这方面的帮助。我也会尝试一下,看看哪种方法最适合。我喜欢上面的 $|++ 提示。我一直在做一个 select(STDOUT) 然后 $|=1
【解决方案3】:
$|++;    
open(my $tar, 'tar ... |') or die "Could not run tar ... - $!";
      while ($file=<$tar>) {
           print "$file";
      }
      print "\n";
      close($tar);

这会打印从 tar 收到的文件名。

【讨论】:

    【解决方案4】:

    对于在长时间运行的任务期间显示进度,您会发现 Term::ProgressBar 很有用——它执行您描述的“在屏幕上打印 #”功能。

    【讨论】:

      【解决方案5】:

      我会尝试这样的事情

      open my $tar, "tar -cvf file.tar..... 2>&/dev/null |"
          or die "can't fork: $!";
      my $i = 0;
      while (<$tar>) {
          if( i++ % 1000 == 0 ) print;
      } 
      close $tar or die "tar error: $! $?";
      

      【讨论】:

      • 请为您的文件/进程句柄使用本地词法,而不是全局词。
      • 我的 perl 有点生疏了,我不知道这对于文件句柄来说是可能的。谢谢,我下次试试。
      【解决方案6】:

      扩展 Hobbs 提供的内容,如果您想将数据从子进程返回到父进程,您需要有一个外部管道。我最终使用了 tempfs,因为它像文件一样简单,但不会将 IO 命中放在磁盘上。

      ** 重要 ** 您需要退出子进程,否则“子”进程将沿同一脚本继续,您将获得双 print 语句。因此,在下面的示例中,foreach (@stdoutput) 会发生两次,尽管只在脚本中出现过一次。

      $shm_id = time;  #get unique name for file - example "1452463743"
      $shm_file = "/dev/shm/$shm_id.tmp";   #set filename in tempfs
      $| = 1;  #suffering from buffering
      print ("Activity Indicator: "); #No new line here
      defined(my $pid = fork) or die "Couldn't fork: $!";
      if (!$pid) { # Child
          @stdoutput=`/usr/home/script.pl -o $parameter`; #get output of external command
          open (SHM, ">$shm_file");
          foreach (@stdoutput) {
              print SHM ("$_");  #populate file in tempfs
          }
          close (SHM);        
          exit;  #quit the child process (will not kill parent script)
      } else { # Parent
          while (! waitpid($pid, WNOHANG)) {
          print ("\#");  # prints a progress bar
              sleep 5;
          }
      }
      print ("\n"); #finish up bar and go to new line
      open (SHM, "$shm_file");
          @stdoutput = <SHM>;  #Now open the file and read it. Now array is in parent
      close (SHM);
      unlink ($shm_file);  #deletes the tempfs file
      chomp(@stdoutput);
      foreach (@stdoutput) {
          print ("$_\n");  #print results of external script
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-08-28
        • 2013-07-13
        • 1970-01-01
        • 1970-01-01
        • 2012-10-22
        • 2010-09-06
        • 2010-09-21
        相关资源
        最近更新 更多