2010-07-07 79 views
7

我有一个执行一些任务的Perl脚本,其中一个是调用system命令到"tar -cvf file.tar....."在Perl中运行system()命令时显示进度

这通常需要一些时间,所以我希望命令行能够回显一个进度指示器,例如#回调到屏幕,而system调用正在进行。

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

我也见过参考waitpid ....我猜我也需要使用它。

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

我吠叫错了树吗?

非常感谢 约翰

+0

'fork'的返回值是孩子的进程ID(如果它是父代的),或者是0(如果它是孩子)或者是undef(如果分叉失败)。请参阅perldoc -f叉。 – 2010-07-07 08:44:00

回答

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: $! $?"; 
+2

请为您的文件/进程句柄使用本地词汇,而不是全局变量。 – Ether 2010-07-07 15:05:43

+0

我的perl有点生疏,我不知道这是可能的文件句柄。谢谢,我会在下次尝试。 – 2010-07-07 15:34:00

7

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); 

的东西,打印散列标记每10至100行左右得到一个不错的gaugebar更换print "."

+0

嗨Johan ...那正是我所追求的!非常感谢您的回复和示例。看到示例代码时,我总是发现它更容易。 Registers 约翰 – john 2010-07-07 09:22:44

+0

一个问题....为什么在打印行上的逗号?这是做什么的? – john 2010-07-07 09:23:48

+0

不需要逗号,我将它从示例中移除;-) – Johan 2010-07-07 09:32:53

6

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

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似乎很有希望为抽象这种工作,但我不知道它是多么可靠。

+0

嗨,谢谢你的帮助。我也会试一试,看看最适合的方法。我喜欢上面的$ | ++提示。我一直在做一个选择(STDOUT),然后$ | = 1 – john 2010-07-07 12:02:33

1

为了在长时间运行的任务中显示进度,您会发现Term::ProgressBar有用 - 它执行所描述的“在屏幕上打印#号”功能。

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

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

0

如果希望从子进程获取数据返回到父进程,您需要扩展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 
} 
相关问题