2017-01-12 80 views
1

我必須從Perl腳本啓動PDF查看器。查看者應該將 從父進程和運行父進程的終端分離。如果我關閉父母或終端,那麼 查看器仍應繼續運行。我考慮三種方法(使用evince作爲PDF查看器命令):從Perl腳本啓動PDF查看器

  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'; 
    } 
    

這些方法有什麼區別?你會推薦哪種方法?

回答

3
system 'evince test.pdf &'; 

根據我的經驗,這很可能就真的是:

system 'evince $pdf_file &'; 

如果$pdf_file是用戶輸入的,那麼我們得到的殼注入漏洞,如傳遞的$(rm -rf /)甚至是PDF格式的名字只是;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重定向。這是很容易添加 - 裏面你如果在Exec之前,只是關閉並重新打開文件句柄,因爲你需要,例如:

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

沒什麼大不了的。然而,Proc :: Daemon確實爲您設置了更多的東西,以確保信號不會從任何一個方向到另一個方向。這取決於你需要得到多嚴厲。

對於我的大部分目的,我發現#2是足夠的。我只達到PROC ::守護程序上的幾個項目,但是這就是一)我有過模塊安裝的完全控制,和b)它真的很重要。啓動PDF查看器通常不會出現這樣的情況。

我避免#1不惜一切代價 - 我曾與外殼注塑一些相當顯著咬傷,現在儘量避免在任何時候都外殼。

+0

這將有趣的,如果這可能與到'nohup'和'setsid'命令 –