2010-06-26 49 views
3

我在寫一個模仿gcc的perl腳本。這是我的腳本需要從gcc處理一些stdout。處理部分已完成,但我無法獲得簡單部分的工作:我如何將所有命令行參數按原樣轉發到下一個進程(在我的情況下爲gcc)。發送給gcc的命令行往往很長,並且可能包含大量的轉義序列,我不想現在通過轉義來玩這個遊戲,而且我知道在複雜的情況下在窗口中正確使用它是非常棘手的。如何獲得整個命令行字符串?

基本上, gcc.pl一些crazies\ t\\ "command line\""和gcc.pl必須轉發相同的命令行到真正的gcc.exe(我使用Windows)。

我這樣做︰open("gcc.exe $cmdline 2>&1 |")所以從gcc stderr被饋送到標準輸出和我的Perl腳本處理標準輸出。問題是,我無法找到任何地方如何構建$cmdline

回答

1

請閱讀exec函數和Perl中的system函數。

如果您提供其中任何一個參數數組(而不是單個字符串),它將直接調用Unix execve()函數或近親,而不會讓shell解釋任何內容,完全按照您需要的做。

+0

這已經很接近,但我原本選擇,因爲我確實需要從外殼的一些援助,以使用開放:我向前錯誤輸出到標準輸出,然後我將stdout提供給我的腳本。系統(如果它和C中的系統一樣)不允許我處理stdout(除非我用std文件描述符做一些黑魔法技巧),exec也不允許我這樣做(顯然)。 此外,我需要逐行處理(我的程序,我用g ++/gcc編譯非常複雜需要幾秒鐘來編譯每個文件,我需要逐行處理,而不是凍結秒,然後轉儲所有stdout。 – PPS 2010-06-26 06:43:48

+0

那麼,在這一點上,我的決定是否正確地使用open或者我需要查看exec/system調用? 似乎引用規則在windows上並不那麼複雜,似乎我應該親手寫它(對於exmaple,php有一個簡單的api用於shell引用,這對我的情況很有用) – PPS 2010-06-26 06:44:15

+1

open接受一個列表,就像exec和system一樣,除非你的perl非常老了。 – hobbs 2010-06-26 12:47:33

2

我會用AnyEvent::Subprocess

use AnyEvent::Subprocess; 

my $process_line = sub { say "got line: $_[0]" }; 

my $gcc = AnyEvent::Subprocess->new(
    code  => ['gcc.exe', @ARGV], 
    delegates => [ 'CompletionCondvar', 'StandardHandles', { 
     MonitorHandle => { 
       handle => 'stdout', 
       callback => $process_line, 
     }}, { 
     MonitorHandle => { 
       handle => 'stderr', 
       callback => $process_line, 
     }}, 
    ], 
); 

my $running = $gcc->run; 
my $done = $running->recv; 
$done->is_success or die "OH NOES"; 

say "it worked"; 

的MonitorHandle委託就像重定向,除非你使用一個單獨的過濾器爲每個輸出和錯誤的選項。 「code」arg是一個表示要運行的命令的arrayref。

+0

這似乎是我需要的;) 謝謝! 我知道許多工具/ sdks使用這種技術,但我沒有任何人做這種類型的轉發,看看他們如何在perl中實現它。我將stderr轉發到stdout,這樣我就不需要單獨處理它們(如果我沒有正確執行,可能會死鎖)。看起來像AnyEvent :: Subprocess是我的正確解決方案。 – PPS 2010-06-26 06:49:08

+0

其實,我只是試過在Windows上安裝AE :: Subprocess,而且事情並不正確。嘆。使用cygwin :) – jrockway 2010-06-26 06:54:56

+0

無法運行,我運行VisualStudio構建過程中的所有垃圾,因此cygwin不是一個選項。我試圖在AE :: Subprocess裏查找它們可能用於引用的特定代碼,但沒有看到任何內容。 – PPS 2010-06-26 06:59:08

0

感謝您的回答,我得出的結論是,我犯了一個很大的錯誤,我再次觸摸perl:幾小時的時間浪費,發現它無法正常完成。 Perl使用不同的方式來分割命令行參數,而不是所有其他使用MS stdlib(在win32上都是標準的)的應用程序。

由於某些命令行參數意圖被解釋爲signle命令行參數,因此perl可以被解釋爲多個參數。這意味着我所要做的就是浪費時間,因爲perl中的錯誤行爲。如果我1)不能像原樣訪問原始命令行,並且2)perl不能正確拆分命令行參數,則無法正確完成此任務。

作爲一個簡單的測試:

script.pl """test |test" 

win32上會錯誤解釋命令行:

ARGV=['"test', '|test'] 

然而,在Windows上正確的 「答案」 必須

ARGV=['"test |test'] 

我用activestate perl,I t ried也草莓perl的最新版本:都吸。看起來,MSI附帶的perl工作正常,很可能是因爲它是針對mingw而不是cygwin運行時構建的?..

perl的問題和原因是它有錯誤的cmd行解析器,它不起作用在Windows上無論什麼cygwin支持與否。 我有一個簡單的情況下的環境變量(我無法控制)擴展到

perl gcc.pl -c "-IC:\ffmpeg\lib_avutil\" rest of args 

的Perl看到的是我有兩個ARGS只:-c和「-IC:\ ffmpeg的\ lib_avutil」 ARGS的其餘 而任何符合Windows實現接收第二個cmd行參數爲:'-IC:\ ffmpeg \ lib_avutil \',這意味着perl是我的簡單情況下的一大堆垃圾,因爲它沒有提供足夠的方法來訪問cmd我最好使用boost :: regex並直接在C++中完成所有的解析工作,至少我不會像ne和!=那樣編寫愚蠢的錯誤來比較字符串等等.Windows的命令行參數的轉義規則是很奇怪,但他們是標準的窗口和Perl因爲一些奇怪的原因不婉轉遵循操作系統的規則。

+0

我看不懂這個。你可以嘗試重新格式化它嗎? – 2010-06-26 08:58:25

+2

你不公正地指責'cmd.exe'在perl上的缺點。命令外殼拆分參數,而不是perl。如果你將'@ ARGV'直接插入到另一個命令中(也就是運行shell的參數解析的另一個*),你將得到不同的結果。 – 2010-06-27 00:28:42

+0

你錯了。命令外殼不分割參數。在Windows進程獲取命令行作爲一個字符串和libc做參數拆分。所有perl在Windows上構建的問題可能是因爲他們使用不同的算法(在Windows世界中是錯誤的)來拆分args(提示:他們需要使用msvcrt lib或寫入符合變體的窗口)。 – PPS 2010-07-16 03:50:58

2

"Safe Pipe Opens"在perlipc文檔中描述瞭如何獲得另一個命令的輸出,而不必擔心shell如何解析它。該技術通常用於安全處理不受信任的輸入,但它也可以避免正確轉義所有參數的容易出錯的任務。

因爲它避開了shell,所以你需要自己創建2>&1的效果,但是正如你在下面看到的那樣,它很簡單。

#! /usr/bin/perl 

use warnings; 
use strict; 

my $pid = open my $fromgcc, "-|"; 
die "$0: fork: $!" unless defined $pid; 

if ($pid) { 
    while (<$fromgcc>) { 
    print "got: $_"; 
    } 
} 
else { 
    # 2>&1 
    open STDERR, ">&STDOUT" or warn "$0: dup STDERR: $!"; 

    no warnings "exec"; # so we can write our own message 
    exec "gcc", @ARGV  or die "$0: exec: $!"; 
} 

的Windows適當does not support open FH, "-|",但Cygwin並很爽快:

$ ./gcc.pl foo.c 
got: gcc: foo.c: No such file or directory 
got: gcc: no input files
+0

dup STDERR to STDOUT就像這樣,你得到兩全其美 – mob 2010-06-26 16:15:45