2017-01-12 6 views
1

Мне нужно запустить программу просмотра PDF из сценария Perl. Зритель должен отсоединиться от родительского процесса и терминала, из которого был запущен родительский процесс. Если я закрываю родительский элемент или терминал, просмотрщик все равно должен работать. Я рассмотрел три подхода (с использованием evince как команда PDF Viewer):Запустите средство просмотра PDF из сценария Perl

  1. Использование system и sh:

    system 'evince test.pdf &'; 
    
  2. Использование fork():

    $SIG{CHLD} = "IGNORE"; #reap children as they complete 
    my $pid = fork(); 
    if ($pid == 0) { 
        exec 'evince', 'test.pdf'; 
    } 
    
  3. Использование Proc::Daemon:

    use Proc::Daemon; 
    my $daemon = Proc::Daemon->new(
        work_dir  => '/tmp/evince', 
        child_STDOUT => '>>stdout.txt', 
        child_STDERR => '>>stderr.txt', 
    ); 
    my $pid = $daemon->Init(); 
    if ($pid == 0) { 
        exec 'evince', 'test.pdf'; 
    } 
    

В чем разница между этими подходами? Какой подход вы бы порекомендовали?

ответ

3
system 'evince test.pdf &'; 

По моему опыту, это, вероятно, действительно:

system 'evince $pdf_file &'; 

Если $pdf_file вводимый пользователем, то мы получим оболочки инъекционные ошибок, например, передавая имя ПДФ $(rm -rf /) или даже только ;rm -rf /. А что, если в названии есть пробел? Ну, вы можете избежать всего этого, если вы его процитируете, верно?

system 'evince "$pdf_file" &'; 

Ну, нет, теперь все, что нужно сделать, это дать вам имя файла ";rm -rf "/. И что, если мой pdf-код имеет двойную кавычку в названии? Вы можете использовать одинарные кавычки, но та же проблема возникает, если в имени файла есть одинарные кавычки, а инъекция оболочки на самом деле не сложнее. Вы можете придумать сложную функцию shellify, которая правильно цитирует всю строку, чтобы оболочка могла ее игнорировать и вернуться к исходной записи ... но это похоже на гораздо большую работу, чем ваши другие параметры, ни одна из которых не страдает от эти проблемы.

$SIG{CHLD} = "IGNORE"; #reap children as they complete 
my $pid = fork(); 
if ($pid == 0) { 
    exec 'evince', 'test.pdf'; 
} 

Настройка глобальной $SIG{CHLD} красиво и легко ... если вам не нужно обращаться с другими детьми, как они умирают. Так что только вы можете сказать, приемлемо это или нет. И, опять же, по моему опыту, даже не всегда. Я был укушен этим - хотя и редко. У меня было это смешение с приложением, которое в другом месте использовалось AnyEvent, и ему удалось сломать обработку подпроцесса AE. (То же самое было бы верно, если бы вы смешали это с какой-либо системой событий, я просто использовал AE.)

Кроме того, это отсутствие перенаправления stdout и stderr - и перенаправление stdin. Это достаточно просто добавить - внутри вашего, если до ехеса, просто закрыть и вновь открыть дескрипторы файлов, как вам нужно, например .:

close STDOUT; open STDOUT, '>', '/dev/null'; 
close STDERR; open STDERR, '>', '/dev/null'; 
close STDIN; open STDIN, '<', '/dev/null'; 

Нет большое значение. Тем не менее, Proc :: Daemon создает для вас еще несколько вещей, чтобы сигналы не доходили от одного к другому процессу в любом направлении. Это зависит от того, насколько сильно вы должны получить.

Для большинства моих целей я нашел № 2, чтобы быть достаточным.Я только сделал для Proc :: Daemon несколько проектов, но вот где a) у меня есть полный контроль над установкой модуля, и b) это действительно имеет значение. Начало просмотра в формате pdf обычно не было бы таким.

Я избегаю # 1 любой ценой - у меня были довольно значительные укусы с инъекцией оболочки, и теперь старайтесь избегать оболочки в любое время.

+0

Было бы интересно, если бы это было связано с командами 'nohup' и' setsid' –