从Perl脚本启动pdf查看器

时间:2017-01-12 22:24:25

标签: perl daemon

我必须从Perl脚本启动一个pdf查看器。观众应该 与父进程和运行父进程的终端分离。如果我关闭父母或终端 观众仍然应该继续运行。我考虑了三种方法(使用evince作为pdf viewer命令):

  1. 使用systemsh

    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';
    }
    
  4. 这些方法之间有什么区别?你会推荐哪种方法?

1 个答案:

答案 0 :(得分:3)

system 'evince test.pdf &';

根据我的经验,这可能真的是:

system 'evince $pdf_file &';

如果$pdf_file是用户输入,那么我们会收到shell注入错误,例如传入pdf名称$(rm -rf /)或甚至;rm -rf /。如果名称中有空格怎么办?好吧,如果引用它,你可以避免所有这些,对吧?

system 'evince "$pdf_file" &';

嗯,不,现在我所要做的就是给你一个";rm -rf "/的文件名。如果我的pdf名字中有双引号怎么办?您可以使用单引号,但如果文件名中包含单引号,则会出现同样的问题,并且shell注入实际上并不困难。你可以想出一个精心设计的shellify函数,它可以正确地引用一个字符串,以便shell可以取消引用它并返回到原始条目......但这似乎比你的其他选项更多的工作,它们都没有受到影响这些问题。

$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重定向。这很容易添加 - 在你的if之前,在exec之前,只需关闭并重新打开你需要的文件句柄,例如:

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 - 我有一些相当重要的咬壳注射,现在尝试避免使用外壳。