簡體   English   中英

如何從 Windows cmd shell 捕獲 output?

[英]How can I capture output from the Windows cmd shell?

有什么辦法,比如 Perl 或 PHP,我可以從另一個輸出到 887414387988 cmd shell 的進程中獲取 output? 我有一個輸出某些信息的游戲服務器,例如說“玩家在 43 秒內完成曲目”,我想抓住該行並使用 Perl 或 PHP 向網絡服務器發送請求以更新 web 頁面上的排名。 有沒有辦法在 Perl 或 PHP 中獲取 output pipe? 或者我可以使用 C++ Windows API 來實現嗎?

Let me clarify here: I want to execute a seperate Perl or PHP script that grabs output from the Windows cmd shell, and the output that is being displayed to the Windows cmd shell is coming from a different process.

您可以使用IPC::Open3從其他進程的標准 output 中讀取。請注意,進程間通信假定進程之間存在父/子關系。 如果不是這種情況......我不知道附加到預先存在的進程的 output 的機制。 在這種情況下,您可能需要更改生產者以將數據寫入您的應用程序可以讀取的日志文件(或數據庫)。

如果您只關心 STDOUT,則可以只使用IPC::Open2 中的 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";
}

您需要在 Perl 內啟動您的服務器:

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

現在 $server_out 包含 server.exe 的 output。 但是這里的技巧是你必須等到 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";
}

注意:代碼未經測試。

這里找到了信息。

在 Perl 中捕獲 output 非常簡單:

$output = qx(command);

要么

$output = `command`;  # backticks

參考: perldoc perlop

此代碼將控制台應用程序的 STDOUT 重定向到一個字符串列表,例如,您可以在備忘錄中使用它。 它是 Delphi 代碼,但在 C++ 中,基本思想是完全一樣的。

我用它來運行隱藏的控制台應用程序,同時將 output 重定向到我自己的應用程序,以顯示在窗格中。 它會在數據輸入后立即向 AStrings 添加新行,因此您可以在它完成之前訪問其他應用程序的 output。

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}

這里給出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() 方法一個一個地發出它們,這樣您還可以處理返回和任何異常。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM