2009-06-16 131 views
1

Perl或PHP有什麼方法可以從另一個輸出到Windows cmd shell的進程獲取輸出嗎?我有一個輸出某些信息的遊戲服務器,例如說'玩家完成了43s的軌道',我想抓住這條線並使用Perl或PHP向網絡服務器發送請求以更新網頁上的排名。有沒有辦法在Perl或PHP中抓取輸出管道?或者我可以實現這一點使用C + + Windows API也許?如何捕獲Windows cmd shell的輸出?

讓我在這裏澄清一下:我想執行一個獨立的Perl或PHP腳本,它抓取Windows cmd shell的輸出,並且顯示給Windows cmd shell的輸出來自不同的進程。

回答

2

您需要Perl中啓動服務器:

my $server_out = `server.exe`; # Note the backticks. 

現在$ server_out包含SERVER.EXE的輸出。但這裏的訣竅是,你必須等到server.exe退出才能完成。

嘗試IPC::Run(這不是一個核心模塊)

use English; 
use IPC::Run; 
my ($stdout, $stderr); 

IPC::Run::run([$cmd, $arg1, $arg2, $argN], \undef, \$stdout, $stderr); 

while(<$stdout>) { 
    print "Cmd said $_\n"; 
} 

注:代碼未經測試。

實測值的信息here.

1

捕捉Perl的輸出是一樣簡單:

$output = qx(command); 

$output = `command`; # backticks 

參見:perldoc perlop

+0

這是行不通的,因爲一旦命令完成我只能訪問到$輸出...和服務器過程中沒有「完成」 – user105033 2009-06-16 16:46:26

4

你可以使用IPC::Open3從另一個進程的標準輸出讀。請注意,進程間通信假定進程之間有父/子關係。如果情況並非如此......我不知道有一種機制可以附加到已有流程的輸出中。在這種情況下,您可能需要更改生產者以將數據寫入日誌文件(或數據庫),以便應用程序可以讀取。

3

如果所有你關心的是STDOUT,你可以只使用open2從IPC::Open2

#!/usr/bin/perl 

use strict; 
use warnings; 

use IPC::Open2; 

#if there are arguments pretend to be the server 
#for this example 
if (@ARGV) { 
    local $| = 1; 
    for my $i (1 .. 100) { 
     print "pid $$ iter $i\n"; 
     sleep 1; 
    } 
    exit; 
}   

#run perl with the current script as its argument, 
#pass in an arg so that we trigger the behaviour 
#above 
open2 my $out, my $in, $^X, $0, 1 
    or die "could not run '$^X $0 1': $!\n"; 

while (<$out>) { 
    s/[\r\n]//g; 
    print "pid $$ saw [$_]\n"; 
} 
+0

,當我回家 – user105033 2009-06-16 18:07:18

+0

我給這個一展身手 - |在Windows下無法使用。 – 2009-06-16 20:36:11

1

此代碼重定向控制檯應用程序的StringList的,你可以在例如備忘錄使用的標準輸出。這是Delphi代碼,但在C++中,基本思想完全相同。

我使用它來隱藏運行控制檯應用程序,同時將輸出重定向到我自己的應用程序,以顯示在窗格中。一旦數據進入,它就會向AStrings添加一條新線,因此您可以在完成之前訪問其他應用程序的輸出。

procedure RunConsoleApp(const CommandLine: string; AStrings: TStrings); 
type 
    TCharBuffer = array[0..MaxInt div SizeOf(Char) - 1] of Char; 
const 
    MaxBufSize = 1024; 
var 
    I: Longword; 
    SI: TStartupInfo; 
    PI: TProcessInformation; 
    SA: PSecurityAttributes; 
    SD: PSECURITY_DESCRIPTOR; 
    NewStdIn: THandle; 
    NewStdOut: THandle; 
    ReadStdOut: THandle; 
    WriteStdIn: THandle; 
    Buffer: ^TCharBuffer; 
    BufferSize: Cardinal; 
    Last: WideString; 
    Str: WideString; 
    ExitCode_: DWORD; 
    Bread: DWORD; 
    Avail: DWORD; 
begin 
    GetMem(SA, SizeOf(TSecurityAttributes)); 

    case Win32Platform of 
    VER_PLATFORM_WIN32_NT: 
     begin 
     GetMem(SD, SizeOf(SECURITY_DESCRIPTOR)); 
     SysUtils.Win32Check(InitializeSecurityDescriptor(SD, SECURITY_DESCRIPTOR_REVISION)); 
     SysUtils.Win32Check(SetSecurityDescriptorDacl(SD, True, nil, False)); 
     SA.lpSecurityDescriptor := SD; 
     end; {end VER_PLATFORM_WIN32_NT} 
    else 
    SA.lpSecurityDescriptor := nil; 
    end; {end case} 

    SA.nLength := SizeOf(SECURITY_ATTRIBUTES); 
    SA.bInheritHandle := True; 

    SysUtils.Win32Check(CreatePipe(NewStdIn, WriteStdIn, SA, 0)); 

    if not CreatePipe(ReadStdOut, NewStdOut, SA, 0) then 
    begin 
    CloseHandle(NewStdIn); 
    CloseHandle(WriteStdIn); 
    SysUtils.RaiseLastWin32Error; 
    end; {end if} 

    GetStartupInfo(SI); 
    SI.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW; 
    SI.wShowWindow := {SW_SHOWNORMAL} SW_HIDE; 
    SI.hStdOutput := NewStdOut; 
    SI.hStdError := NewStdOut; 
    SI.hStdInput := NewStdIn; 

    if not CreateProcess(nil, PChar(CommandLine), nil, nil, True, CREATE_NEW_CONSOLE, nil, nil, SI, PI) then 
    begin 
    CloseHandle(NewStdIn); 
    CloseHandle(NewStdOut); 
    CloseHandle(ReadStdOut); 
    CloseHandle(WriteStdIn); 
    SysUtils.RaiseLastWin32Error; 
    end; {end if} 

    Last := ''; 
    BufferSize := MaxBufSize; 
    Buffer := AllocMem(BufferSize); 

    try 
    repeat 
     SysUtils.Win32Check(GetExitCodeProcess(PI.hProcess, ExitCode_)); 
     PeekNamedPipe(ReadStdOut, Buffer, BufferSize, @Bread, @Avail, nil); 

     if (Bread <> 0) then 
     begin 
     if (BufferSize < Avail) then 
     begin 
      BufferSize := Avail; 
      ReallocMem(Buffer, BufferSize); 
     end; {end if} 
     FillChar(Buffer^, BufferSize, #0); 
     Windows.ReadFile(ReadStdOut, Buffer^, BufferSize, Bread, nil); 
     Str := Last; 
     I := 0; 

     while (I < Bread) do 
     begin 

      case Buffer^[I] of 
      #0: inc(I); 
      #7: begin 
        inc(I); 
        Windows.Beep(800, 50); 
        Str := Str + '^'; 
       end; 
      #10: 
       begin 
       inc(I); 
       AStrings.Add(Str); 
       Str := ''; 
       end; {end #10} 
      #13: 
       begin 
       inc(I); 
       if (I < Bread) and (Buffer^[I] = #10) then 
        inc(I); 
       AStrings.Add(Str); 
       Str := ''; 
       end; {end #13} 
      else 
      begin 
       Str := Str + Buffer^[I]; 
       inc(I); 
      end; {end else} 
      end; {end case} 
     end; {end while} 
     Last := Str; 
     end; {end if} 
     Sleep(1); 
     Application.ProcessMessages; 

    until (ExitCode_ <> STILL_ACTIVE); 

    if Last <> '' then 
     AStrings.Add(Last); 

    finally 
    FreeMem(Buffer); 
    end; {end try/finally} 

    CloseHandle(PI.hThread); 
    CloseHandle(PI.hProcess); 
    CloseHandle(NewStdIn); 
    CloseHandle(NewStdOut); 
    CloseHandle(ReadStdOut); 
    CloseHandle(WriteStdIn); 

end; {end procedure} 
0

這裏是一個PHP特定的解決方案,該項目允許PHP獲得和動態與真實的CMD端子交互。在這裏獲得:https://github.com/merlinthemagic/MTS

下載您只需使用下面的代碼後:

//if you prefer Powershell, replace 'cmd' with 'powershell' 
$shellObj = \MTS\Factories::getDevices()->getLocalHost()->getShell('cmd'); 

$strCmd1 = 'some_app.exe -param "test"'; 
$return1 = $shellObj->exeCmd($strCmd1); 

返回會給你從CMD命令返回或錯誤,就如同您坐在控制檯。此外,您可以針對$ shellObj發出任何您喜歡的命令,該環境在PHP腳本的整個生命週期內都會維護。因此,不要將腳本文件綁定到腳本文件中,只需使用exeCmd()方法逐個發佈它們,這樣您還可以處理返回和任何異常。