2009-06-16 7 views
1

Existe-t-il un moyen avec, disons Perl ou PHP, que je puisse récupérer la sortie d'un autre processus qui sort vers le shell de cmd de Windows? J'ai un serveur de jeu qui produit certaines informations, par exemple, "lecteur terminé en 43s" et je veux saisir cette ligne et utiliser Perl ou PHP pour envoyer une requête à un serveur web pour mettre à jour les rangs sur une page web. Y at-il un moyen d'attraper ce tuyau de sortie en Perl ou PHP? Ou pourrais-je réaliser cela en utilisant C++ Windows API peut-être? Permettez-moi de clarifier ici: Je veux exécuter un script Perl ou PHP séparé qui saisit la sortie du shell cmd de Windows, et la sortie qui est affichée dans le shell cmd Windows provient d'un processus différent.Comment puis-je capturer la sortie du shell cmd Windows?

Répondre

2

Vous devez démarrer votre serveur dans Perl:

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

maintenant $ server_out contient la sortie de Server.exe. Mais l'astuce ici est que vous devez attendre jusqu'à ce que Server.exe quitte pour faire le eteint.

Essayez IPC::Run (ce qui est un module de base)

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

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

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

Nota: Le code non testé.

trouvé l'info here.

1

capture la sortie en Perl est aussi simple que:

$output = qx(command); 

ou

$output = `command`; # backticks 

Reportez-vous: perldoc perlop

+0

Cela ne fonctionnera pas parce que je n'aurai accès à $ sortie une fois la commande terminée ... et le processus du serveur ne «termine» – user105033

4

Vous pouvez utiliser IPC::Open3 pour lire de l'autre processus de sortie standard. Notez que la communication inter-processus suppose une relation parent/enfant entre les processus. Si ce n'est pas le cas ... je ne suis pas au courant d'un mécanisme pour attacher à la sortie d'un processus préexistant. Dans ce cas, vous devrez peut-être modifier le producteur pour écrire des données dans un fichier journal (ou base de données) que votre application peut lire.

3

Si tout ce que vous aimez est STDOUT, vous pouvez simplement utiliser open2 de 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

Je vais l'essayer quand je rentre à la maison – user105033

+0

- | ne fonctionnera pas sous Windows. –

+0

@Alexandr Ciornii Wow, ça craint, au moins il y a IPC :: Open2 et IPC :: Open3. –

1

Ce code redirige le STDOUT d'une application console à un stringlist, que vous pouvez utiliser sur une note de service par exemple. C'est du code Delphi, mais en C++, l'idée de base est exactement la même.

Je l'utilise pour exécuter des applications de la console cachées, tout en redirigeant la sortie vers ma propre application pour afficher dans une fenêtre. Il ajoute une nouvelle ligne à AStrings dès que les données arrive, donc vous aurez accès à la sortie de l'autre application avant qu'elle ne se termine.

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

Si quelqu'un pouvait convertir cela en RPC, ce serait Awsome. – user105033

0

Voici une solution spécifique PHP, le projet permet à PHP d'obtenir et d'interagir dynamiquement avec un terminal réel cmd.Obtenez ici: https://github.com/merlinthemagic/MTS

Après avoir téléchargé vous suffit d'utiliser le code suivant:

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

Le retour vous donnera le retour de commande ou erreur de cmd, comme si vous étiez assis à la console.

En outre, vous pouvez émettre n'importe quelle commande contre le $ shellObj, l'environnement est maintenu tout au long de la vie du script PHP. Ainsi, au lieu de regrouper les commandes dans un fichier de script, il suffit de les exécuter une par une à l'aide de la méthode exeCmd(), de cette façon vous pouvez également gérer le retour et toutes les exceptions.

Questions connexes