2009-05-26 1 views
10

У меня есть некоторый код Perl, который выполняет сценарий оболочки для нескольких параметров, чтобы упростить, я просто предположу, что у меня есть код, который выглядит следующим образом:Как я могу заставить Perl ждать дочерних процессов, запущенных в фоновом режиме с помощью system()?

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

Я хотел бы сделать еще несколько вещей после это, но я не могу найти способ дождаться завершения всех дочерних процессов, прежде чем продолжить.

Преобразование кода для использования fork() будет затруднительным. Разве нет более простого пути?

+0

Вам нужен фактический код возврата из файла file.sh? Можете ли вы изменить сценарий так, чтобы он записывался в файл по завершении? – mkb

+1

Почему было бы сложно преобразовать код в fork? Скрыть все детали в подпрограмме. Вы можете даже переопределить system(), чтобы вместо этого вызывать свою новую подпрограмму. –

+0

Я, вероятно, просто разделил эту часть на отдельный скрипт, который делает разветвление, и вызовет это из системного вызова ... –

ответ

16

Используя вилку/exec/wait не так уж плохо:

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

Вы не проверяете неисправную вилку, и вы выталкиваете pid puzzle на @pids, а не $ pid. Вероятно, выход должен быть штампом, если он выполняется, это означает, что exec завершился с ошибкой. –

+0

Исправлено. Спасибо, я не являюсь носителем языка Perl. – Dave

+0

$ pid == -1, вероятно, должен быть определен ($ pid). –

8

Преобразование в fork() может быть затруднено, но это правильный инструмент. system() - блокирующий вызов; вы получаете неблокирующее поведение, выполняя оболочку и сообщая ей, чтобы запустить ваши сценарии в фоновом режиме. Это означает, что Perl не знает, что такое PID от детей, а это значит, что ваш скрипт не знает, чего ждать.

Вы можете попытаться передать PID до Perl-скрипта, но это быстро выходит из-под контроля. Используйте fork().

13

Вам нужно что-то изменить, изменение кода для использования fork, вероятно, проще, но если вы мертвы против использования fork, вы можете использовать сценарий оболочки оболочки, который касается файла, когда это будет сделано, а затем проверьте свой код Perl на наличие файлов.

Вот обертка:

#!/bin/bash 

$* 

touch /tmp/$2.$PPID 

Ваш код Perl будет выглядеть следующим образом:

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

Но я думаю, что код разветвление безопаснее и понятнее:

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

Это гораздо лучший ответ. – zdim

+0

Очевидно, что это создает много процессов зомби. https://en.wikipedia.org/wiki/Zombie_process –

+1

@PratyushRathore Не следует. Зомби возникает, когда родительский процесс не вызывает wait или waitpid. Как вы можете видеть, родительский вызов waitpid для дочернего, который вышел. Если вы получаете кучу зомби, то либо вы делаете что-то неправильно, либо один из первых детей занимает много времени, а более поздние дети выходят (но не получают). Вы можете переключиться на 'while (waitpid (-1, WNOHANG)> 0) {sleep 1}', чтобы получить следующий оставшийся ребенок. –