2010-07-07 47 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 
} 
相關問題