如何从Windows cmd shell捕获输出?

时间:2009-06-16 16:21:30

标签: php c++ windows perl

有没有办法,比如Perl或PHP,我可以从另一个输出到Windows cmd shell的进程中获取输出?我有一个输出某些信息的游戏服务器,比如说“玩家完成了43s的跟踪”,我想抓住那条线并使用Perl或PHP向网络服务器发送请求以更新网页上的排名。有没有办法在Perl或PHP中获取输出管道?或者我可以使用C ++ Windows API实现这一点吗?

我在这里澄清一下:我想执行一个单独的Perl或PHP脚本来抓取Windows cmd shell的输出,并且显示给Windows cmd shell的输出来自不同的进程。

6 个答案:

答案 0 :(得分:4)

您可以使用IPC::Open3从其他进程的标准输出中读取。请注意,进程间通信假定进程之间存在父/子关系。如果不是这样的话......我不知道附加到预先存在的进程的输出的机制。在这种情况下,您可能需要更改生产者以将数据写入应用程序可以读取的日志文件(或数据库)。

答案 1 :(得分:3)

如果你关心的只是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";
}

答案 2 :(得分: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.

答案 3 :(得分:1)

在Perl中捕获输出非常简单:

$output = qx(command);

$output = `command`;  # backticks

参考:perldoc perlop

答案 4 :(得分:1)

此代码将控制台应用程序的STDOUT重定向到字符串列表,您可以在备忘录中使用该字符串列表。它是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}

答案 5 :(得分: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()方法逐个发出命令,这样您也可以处理返回和任何异常。