Заставить Perl вызывать подпрограммы END при завершении exec ()? - PullRequest
3 голосов
/ 31 января 2012

Когда вы используете exec () в Perl:

Обратите внимание, что exec не будет вызывать ваши блоки END и не будет вызывать методы DESTROY для ваших объектов.

Как заставить perl вызывать блоки END в любом случае? Могу ли я сделать что-то вроде END(); exec($0) или что-то еще?

Я действительно пытаюсь заставить программу завершить свой текущий экземпляр и запустить совершенно новый экземпляр самого себя, и мне лень это делать правильно (используя cron или помещая всю программу в бесконечный цикл). Однако мои подпрограммы END очищают временные файлы и другие важные вещи, поэтому мне нужно, чтобы они выполнялись между выполнениями.

Бесполезные ссылки на код:

https://github.com/barrycarter/bcapps/blob/master/bc-metar-db.pl

https://github.com/barrycarter/bcapps/blob/master/bc-voronoi-temperature.pl

https://github.com/barrycarter/bcapps/blob/master/bc-delaunay-temperature.pl

Ответы [ 4 ]

10 голосов
/ 31 января 2012

То есть вы пытаетесь выполнить программу в своем скрипте? exec вероятно, не то, что вы хотите. exec ведет себя как C exec: то, что называется заменяет ваш текущий процесс; чтобы продолжить, вам нужно сделать что-то вроде fork, чтобы сохранить текущий процесс при выполнении другого.

Но хорошие новости! Это все существует во встроенном system.

Действует точно так же, как exec LIST, за исключением того, что сначала выполняется форк, и родительский процесс ожидает завершения дочернего процесса.

Вот как это выглядит:

use 5.012; # or use 5.012 or newer
use warnings;

... # some part of my program

system($my_command, $arg1, $arg2); # forks, execs, returns.

END {
    # still gets called because you never left the script.
}

Если вам абсолютно необходимо использовать exec, вы должны автоматически вызвать процедуру очистки. Чтобы узнать больше о END, см. perldoc perlmod для получения полной информации. Суть этого: END - это один из нескольких типов блоков кода, который выполняется на определенном этапе выполнения скрипта. Они НЕ подпрограммы. Однако вы можете выполнить любой код, который вы хотите в этих подпрограммах. Так что вы можете сделать:

sub cleanup { ... } # your cleanup code

sub do_exec {
    cleanup();
    exec( ... );
}

END {
    cleanup();
}

и тогда вы знаете, что ваш код очистки будет выполнен при выходе из скрипта ИЛИ при выполнении вашего exec.

6 голосов
/ 31 января 2012

Чтобы ответить на узкий вопрос о том, как вызывать ваши блоки END в произвольные моменты времени, вы можете использовать метод B::end_av с B::SV::object_2svref, чтобы получить ссылки на код для ваших блоков END .

sub invoke_end_blocks_before_exec {
    use B;
    my @ENDS = B::end_av->ARRAY;
    foreach my $END (@ENDS) {
        $END->object_2svref->();
    }
}

END { print "END BLOCK 1\n" }
END { print "END BLOCK 2\n" }

...

invoke_end_blocks_before_exec();
exec("echo leave this program and never come back");

Выход:

END BLOCK 2
END BLOCK 1
leave this program and never come back

Я обычно предпочитаю что-то менее волшебное. Почему не такая структура, как

sub cleanup { ... }
END { &cleanup }

if (need_to_exec()) {
    cleanup();        # same thing END was going to do anyway
    exec( ... );
}

4 голосов
/ 31 января 2012

Форк и exec

Это оставит вас с новым pid, но вы можете сделать fork / exec:

my $pid = fork();
defined $pid or die "fork failed";
exit if $pid; # parent immediately exits, calling END blocks.
exec($0) or die "exec failed"; # child immediately execs, will not call END blocks (but parent did, so OK)

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

Обернуть вашу программу

Кроме того, банально просто обернутьваша Perl-программа в сценарии оболочки (или Perl), который выглядит примерно так:

#!/bin/sh

while sleep 5m; do
    perl your-program.pl
done

или

#!/usr/bin/perl

while (1) {
    system("perl your-program.pl");
    sleep(5*60);
}
2 голосов
/ 31 января 2012

Можете ли вы положить свой вызов на exec в конце (финального) END блока? Там, где ваш текущий вызов exec, установите флаг, затем exit. В конце блока END установите флажок и, если он истинный, позвоните туда exec. Таким образом, вы можете выйти из сценария без перезапуска, если это необходимо, и при этом выполнить блоки END.

Тем не менее, я бы рекомендовал не реализовывать этот тип хвостовой рекурсии на уровне процесса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...