2010-07-07 35 views
7

Tôi có một tập lệnh Perl thực hiện một số tác vụ, một trong số đó là gọi một lệnh system tới "tar -cvf file.tar.....". Điều này thường có thể mất một thời gian vì vậy tôi muốn dòng lệnh để echo trở lại một chỉ số tiến bộ, một cái gì đó giống như một # vang vọng trở lại màn hình trong khi các cuộc gọi system đang được tiến hành.Hiển thị tiến trình trong khi chạy lệnh system() trong Perl

Tôi đã thực hiện một số thao tác xung quanh và tình cờ gặp fork. Đây có phải là cách tốt nhất để đi không? Có thể ngã ba khỏi lệnh system, sau đó tạo một vòng lặp while để kiểm tra tình trạng của $pid được trả về bởi ngã ba không?

Tôi cũng đã xem các tham chiếu đến waitpid .... Tôi đoán là tôi cũng cần sử dụng tính năng này.

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

Tôi có đang sủa nhầm cây không?

Rất cám ơn John

+0

Giá trị trả lại của 'ngã ba' là ID tiến trình của trẻ, nếu đó là cha mẹ hoặc 0 nếu đó là con hoặc không xác định nếu ngã ba không thành công. Xem ngã ba perldoc -f. –

Trả lời

1

tôi sẽ cố gắng một cái gì đó như thế này

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

Vui lòng sử dụng từ vựng địa phương cho các tập tin của bạn/xử lý quy trình, chứ không phải hình cầu. – Ether

+0

perl của tôi là một chút gỉ, tôi không biết điều này là có thể cho filehandles. Cảm ơn, tôi sẽ thử lần sau. –

7

Perl có một công trình đẹp cho điều này, được gọi là "ống mở ra." Bạn có thể đọc thêm về nó bằng cách gõ perldoc -f open tại dấu nhắc trình bao.

# 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 ...; 

Dưới đây là một đoạn mã cho thấy một ví dụ:

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

Thay print "." với cái gì đó in một dấu băm mỗi 10 đến 100 dòng hoặc lâu hơn để có được một gaugebar tốt đẹp.

+0

Xin chào Johan ... đó chính xác là những gì tôi theo sau! Rất cám ơn câu trả lời và ví dụ của bạn. Tôi luôn thấy dễ dàng hơn khi nhìn thấy mã ví dụ. Kính trọng John – john

+0

một câu hỏi .... tại sao dấu phẩy trên dòng in? Cái đó làm cái gì? – john

+0

Dấu phẩy là không cần thiết, tôi đã xóa nó khỏi ví dụ ;-) – Johan

6

Một ví dụ mà không phụ thuộc vào quá trình con viết bất kỳ loại đầu ra, và chỉ in một dấu chấm khoảng một lần một giây miễn là nó đang chạy:

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"; 
} 

Mặc dù nó có thể chịu đựng để có kiểm tra lỗi nhiều hơn và có có thể thực sự là một cái gì đó tốt hơn đã có trên CPAN. Proc::Background dường như đầy hứa hẹn cho việc trừu tượng hóa loại công việc này nhưng tôi không chắc nó đáng tin cậy như thế nào.

+0

Xin chào và cảm ơn sự giúp đỡ của bạn về điều này. Tôi sẽ thử cái này và xem phương pháp nào phù hợp nhất. Tôi thích mẹo $ | ++ của bạn ở trên. Tôi đã thực hiện một lựa chọn (STDOUT) và sau đó $ | = 1 – john

1

Để hiển thị tiến trình trong khi thực hiện tác vụ dài hạn, bạn sẽ tìm thấy Term::ProgressBar hữu ích - chức năng "in # trên màn hình" mà bạn mô tả.

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

Điều này sẽ in tên tệp nhận được từ tar.

0

Mở rộng những gì Hobbs cung cấp nếu bạn muốn lấy dữ liệu từ quy trình con trở lại quy trình Gốc, bạn cần có ống dẫn bên ngoài. Tôi đã kết thúc bằng cách sử dụng tempfs bởi vì nó đã được đơn giản như một tập tin, nhưng không đặt IO hit trên đĩa.

** Quan trọng ** Bạn cần thoát khỏi tiến trình con, bởi vì nếu không quy trình "con" sẽ tiếp tục trong cùng một tập lệnh và bạn sẽ nhận được các tuyên bố kép print. Vì vậy, trong ví dụ dưới đây foreach (@stdoutput) sẽ xảy ra hai lần mặc dù chỉ có trong kịch bản một lần.

$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 
} 
Các vấn đề liên quan