在Perl中运行system()命令时显示进度

在Perl中运行system()命令时显示进度,perl,Perl,我有一个执行一些任务的Perl脚本,其中一个是调用系统命令“tar-cvf file.tar…” 这通常需要一些时间,因此我希望命令行回显进度指示器,例如在系统调用进行时回显到屏幕 我一直在四处挖掘,无意中发现了fork。这是最好的方式吗?是否可以分叉系统命令,然后创建while循环,检查分叉返回的$pid的状态 我还看到了对waitpid的引用。。。。我猜我也需要用这个 fork system("tar ... ") while ( forked process is still active

我有一个执行一些任务的Perl脚本,其中一个是调用
系统
命令
“tar-cvf file.tar…”

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

我一直在四处挖掘,无意中发现了
fork
。这是最好的方式吗?是否可以分叉
系统
命令,然后创建while循环,检查分叉返回的
$pid
的状态

我还看到了对
waitpid
的引用。。。。我猜我也需要用这个

fork system("tar ... ")
while ( forked process is still active) {
    print #
    sleep 1
}
我找错树了吗

非常感谢
约翰

我想试试这样的东西

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: $! $?";
打开我的$tar,“tar-cvf file.tar…..2>&/dev/null”
或者死“不能叉:$!”;
我的$i=0;
而(){
如果(i++%1000==0)打印;
} 
关闭$tar或死“tar错误:$!$?”;

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 ...;
下面是一个示例片段:

  open(my $tar, '-|', 'tar', ...) or die "Could not run tar ... - $!";
  while (<$tar>) {
       print ".";
  }
  print "\n";
  close($tar);
open(我的$tar'-|','tar',…)或die“无法运行tar…-$!”;
而(){
“打印”;
}
打印“\n”;
收盘价($焦油);

将“打印”替换为每隔10到100行左右打印一个哈希标记,以获得一个漂亮的标尺。

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

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上可能已经有了更好的东西。将这类工作抽象出来似乎很有希望,但我不确定它有多可靠。

对于在长时间运行的任务中显示进度,您会发现它很有用——它可以实现您描述的“跨屏幕打印”功能。

$|++;
$|++;    
open(my $tar, 'tar ... |') or die "Could not run tar ... - $!";
      while ($file=<$tar>) {
           print "$file";
      }
      print "\n";
      close($tar);
打开(我的$tar,'tar…|')或死亡“无法运行tar…-$!”; 而($file=){ 打印“$file”; } 打印“\n”; 收盘价($焦油);

这将打印从tar接收的文件名。

扩展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
}
$shm\u id=时间#获取文件的唯一名称-示例“1452463743”
$shm_file=“/dev/shm/$shm_id.tmp”#以tempfs格式设置文件名
$| = 1;  #饱受缓冲之苦
打印(“活动指示器:”)#这里没有新线路
已定义(my$pid=fork)或死“无法分叉:$!”;
如果(!$pid){Child
@stdoutput=`/usr/home/script.pl-o$parameter`#获取外部命令的输出
打开(SHM,“>$SHM_文件”);
foreach(@stdoutput){
打印SHM(“$”)#以tempfs填充文件
}
关闭(SHM);
退出;#退出子进程(不会终止父脚本)
}否则{#家长
而(!waitpid($pid,WNOHANG)){
打印(\ \ \ \);打印进度条
睡眠5;
}
}
打印(“\n”)#完成酒吧,进入新线
打开(SHM,“$SHM_文件”);
@标准输出=#现在打开文件并读取它。现在数组位于父级
关闭(SHM);
取消链接($shm_文件)#删除tempfs文件
chomp(@stdoutput);
foreach(@stdoutput){
打印(“$\un”);#打印外部脚本的结果
}

fork的返回值是子进程ID(如果是父进程),如果是子进程ID,则返回0,如果fork失败,则返回未定义。见perldoc-f fork。嗨,约翰……这正是我想要的!非常感谢您的回复和示例。当我看到示例代码时,我总是发现这要容易得多。关于john不需要逗号,我从示例中删除了它;-):-)-谢谢你,约翰。有一件事……这个方法是否只适用于向STDOUT写入内容的命令?使用tar zcvf时,它似乎工作得很好,但我使用的另一个命令不会将任何内容回显到STDOUT…..当我尝试打开此命令的管道时,我看不到打印内容“:(@john yes,只有在命令写入内容时才有效。如果子进程不写入任何内容,则需要使用不同的构造,如
fork
/
exec
/nonblocking
waitpid
/
sleep
@Johan如果使用
$+
,则您的代码示例会更好,以便立即打印点y、 :)您好,谢谢您在这方面的帮助。我也会尝试一下,看看哪种方法最适合。我喜欢上面的$|++提示。我一直在做一个选择(STDOUT)然后$|=1请对文件/进程句柄使用本地词汇表,而不是全局词汇表。我的perl有点生疏,我不知道这对文件句柄是可能的。谢谢,我下次会尝试。