简体   繁体   English

如何从 Windows cmd shell 捕获 output?

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

Is there any way with, say Perl or PHP, that I can grab output from another process that outputs to the Windows cmd shell?有什么办法,比如 Perl 或 PHP,我可以从另一个输出到 887414387988 cmd shell 的进程中获取 output? I have a game server that outputs certain information, for example say 'player finished track in 43s' and I want to grab that line and use Perl or PHP to send a request to a webserver to update ranks on a web page.我有一个输出某些信息的游戏服务器,例如说“玩家在 43 秒内完成曲目”,我想抓住该行并使用 Perl 或 PHP 向网络服务器发送请求以更新 web 页面上的排名。 Is there a way to grab that output pipe in Perl or PHP?有没有办法在 Perl 或 PHP 中获取 output pipe? Or could I achieve this using C++ Windows API maybe?或者我可以使用 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. 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.

You could use IPC::Open3 to read from the other process' standard output. Note that inter-process communication assumes a parent/child relationship between the processes.您可以使用IPC::Open3从其他进程的标准 output 中读取。请注意,进程间通信假定进程之间存在父/子关系。 If that's not the case... I'm not aware of a mechanism for attaching to the output of a pre-existing process.如果不是这种情况......我不知道附加到预先存在的进程的 output 的机制。 In that case you may need to alter the producer to write data to a log file (or database) that your application can read from.在这种情况下,您可能需要更改生产者以将数据写入您的应用程序可以读取的日志文件(或数据库)。

If all you care about is STDOUT, you can just use open2 from IPC::Open2 :如果您只关心 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";
}

You need to start your server within Perl:您需要在 Perl 内启动您的服务器:

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

Now $server_out contains the output of server.exe.现在 $server_out 包含 server.exe 的 output。 But the trick here is that you have to wait until server.exe exits to get the out put.但是这里的技巧是你必须等到 server.exe 退出才能得到输出。

Try IPC::Run (which is not a core module)尝试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";
}

Note: Code not tested.注意:代码未经测试。

Found the info here.这里找到了信息。

Capturing the output in Perl is as simple as:在 Perl 中捕获 output 非常简单:

$output = qx(command);

or要么

$output = `command`;  # backticks

Refer: perldoc perlop参考: perldoc perlop

This code redirects the STDOUT of a console application to a stringlist, which you can use on a memo for example.此代码将控制台应用程序的 STDOUT 重定向到一个字符串列表,例如,您可以在备忘录中使用它。 It's Delphi code, but in C++ the basic idea is exactly the same.它是 Delphi 代码,但在 C++ 中,基本思想是完全一样的。

I use it to run console applications hidden, while redirecting the output to my own application, to show in a pane.我用它来运行隐藏的控制台应用程序,同时将 output 重定向到我自己的应用程序,以显示在窗格中。 It adds a new line to AStrings as soon as data comes in, so you'll have access to the output of the other application before it finishes.它会在数据输入后立即向 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}

Here is a PHP specific solution, the project allows PHP to obtain and interact dynamically with a real cmd terminal.这里给出PHP的具体解决方案,该项目允许PHP获取真实的cmd终端并动态交互。 Get it here: https://github.com/merlinthemagic/MTS在这里获取: https://github.com/merlinthemagic/MTS

After downloading you would simply use the following code:下载后,您只需使用以下代码:

//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);

The return will give you the command return OR error from cmd, just as if you sat at the console.返回将为您提供来自 cmd 的命令返回或错误,就像您坐在控制台前一样。

Furthermore, you can issue any command you like against the $shellObj, the environment is maintained throughout the life of the PHP script.此外,您可以对 $shellObj 发出您喜欢的任何命令,该环境在 PHP 脚本的整个生命周期内得到维护。 So instead of bundling commands in a script file, just issue them one by one using the exeCmd() method, that way you can also handle the return and any exceptions.因此,不要将命令捆绑在脚本文件中,只需使用 exeCmd() 方法一个一个地发出它们,这样您还可以处理返回和任何异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM