2009-05-26 30 views
10

Tôi có một số mã Perl mà thực hiện một kịch bản shell cho nhiều thông số, để đơn giản hóa, tôi sẽ chỉ cho rằng tôi có mã trông như thế này:Làm thế nào tôi có thể làm cho Perl chờ đợi cho các tiến trình con bắt đầu trong nền với hệ thống()?

for $p (@a){ 
    system("/path/to/file.sh $p&"); 
} 

Tôi muốn làm một số việc hơn sau điều đó, nhưng tôi không thể tìm được cách để chờ tất cả các quy trình con hoàn thành trước khi tiếp tục.

Việc chuyển đổi mã để sử dụng fork() sẽ rất khó khăn. Không phải là một cách dễ dàng hơn?

+0

Bạn có yêu cầu mã trả lại thực tế từ tệp.sh không? Bạn có thể thay đổi kịch bản để nó ghi vào một tập tin khi hoàn thành? – mkb

+1

Tại sao chuyển đổi mã thành ngã ba là khó khăn? Ẩn tất cả các chi tiết trong một chương trình con. Bạn thậm chí có thể xác định lại hệ thống() để gọi chương trình con mới của bạn thay thế. –

+0

Tôi có lẽ sẽ chỉ chia phần này thành một tập lệnh riêng biệt mà không làm giả mạo, và gọi đó từ cuộc gọi hệ thống ... –

Trả lời

16

Dùng nĩa/exec/wait không quá tệ:

my @a = (1, 2, 3); 
for my $p (@a) { 
    my $pid = fork(); 
    if ($pid == -1) { 
     die; 
    } elsif ($pid == 0) { 
     exec '/bin/sleep', $p or die; 
    } 
} 
while (wait() != -1) {} 
print "Done\n"; 
+1

Bạn không kiểm tra cho một ngã ba không thành công và bạn đang đẩy pew bareword lên @ pids, chứ không phải $ pid. Lối ra có lẽ sẽ là một cái chết thay vào đó, nếu nó thực hiện điều đó có nghĩa là exec đã thất bại. –

+0

Đã sửa lỗi. Cảm ơn, tôi không thực sự là một người bản ngữ của Perl. – Dave

+0

$ pid == -1 có lẽ nên được xác định ($ pid) thay thế. –

8

Chuyển đổi thành ngã ba() có thể khó, nhưng đó là công cụ chính xác. system() là một cuộc gọi chặn; bạn đang nhận được hành vi không chặn bằng cách thực hiện một trình bao và yêu cầu nó chạy các tập lệnh của bạn trong nền. Điều đó có nghĩa là Perl không biết PID của trẻ có thể là gì, điều đó có nghĩa là kịch bản của bạn không biết phải chờ đợi điều gì.

Bạn có thể cố gắng truyền đạt các PID đến kịch bản Perl, nhưng nhanh chóng bị mất. Sử dụng fork().

13

Bạn sẽ phải thay đổi thứ gì đó, thay đổi mã để sử dụng fork có thể đơn giản hơn, nhưng nếu bạn đã chết trước khi sử dụng fork, bạn có thể sử dụng script shell wrapper khi nó được thực hiện và sau đó có kiểm tra mã Perl của bạn cho sự tồn tại của các tập tin.

Đây là wrapper:

#!/bin/bash 

$* 

touch /tmp/$2.$PPID 

Perl code của bạn sẽ trông giống như:

for my $p (@a){ 
    system("/path/to/wrapper.sh /path/to/file.sh $p &"); 
} 
while (@a) { 
    delete $a[0] if -f "/tmp/$a[0].$$"; 
} 

Nhưng tôi nghĩ rằng mã forking là an toàn hơn và rõ ràng hơn:

my @pids; 
for my $p (@a) { 
    die "could not fork" unless defined(my $pid = fork);\ 
    unless ($pid) { #child execs 
     exec "/path/to/file.sh", $p; 
     die "exec of file.sh failed"; 
    } 
    push @pids, $pid; #parent stores children's pids 
} 

#wait for all children to finish 
for my $pid (@pids) { 
    waitpid $pid, 0; 
} 
+0

Đây là câu trả lời hay hơn nhiều. – zdim

+0

Rõ ràng làm điều này tạo ra rất nhiều quá trình zombie. https://en.wikipedia.org/wiki/Zombie_process –

+1

@PratyushRathore Không nên. Một kết quả zombie khi quá trình cha mẹ không gọi chờ đợi hoặc chờ đợi. Như bạn có thể thấy phụ huynh đang gọi waitpid cho một đứa trẻ đã thoát. Nếu bạn đang nhận được một loạt các zombies sau đó hoặc là bạn đang làm một cái gì đó sai hoặc một trong những đứa trẻ đầu tiên là mất một thời gian dài và những đứa trẻ sau này đã thoát (nhưng không được gặt hái). Bạn có thể chuyển sang 'while (waitpid (-1, WNOHANG)> 0) {sleep 1}' để gặt hái đứa trẻ đã thoát kế tiếp. –

Các vấn đề liên quan