Difference between revisions of "Executing External Programs/de"

From Lazarus wiki
Jump to navigationJump to search
m (→‎SysUtils.ExecuteProcess: syntax highlighting)
Line 24: Line 24:
 
Man kann TProcess benutzen, um externe Programme zu starten. Vorteile von TProcess sind
 
Man kann TProcess benutzen, um externe Programme zu starten. Vorteile von TProcess sind
  
*Plattform unabhängig
+
*Plattform-Unabhängigkeit
*Es besteht die Möglichkeit, von stdout zu lesen und auf stdin zu schreiben.
+
*die Fähigkeit, von ''stdout'' zu lesen und auf ''stdin'' zu schreiben.
  
 
Anmerkung: TProcess ist kein Terminal bzw. keine Shell! Weder können Scripte direkt ausgeführt werden, noch kann die Ausgabe mit Operatoren wie "|", ">", "<", "&" etc. umgeleitet werden. Es ist möglich, die gleichen Ergebnisse mit TProcess und FreePascal zu erziehen. Ein paar Beispiele finden Sie weiter unten.
 
Anmerkung: TProcess ist kein Terminal bzw. keine Shell! Weder können Scripte direkt ausgeführt werden, noch kann die Ausgabe mit Operatoren wie "|", ">", "<", "&" etc. umgeleitet werden. Es ist möglich, die gleichen Ergebnisse mit TProcess und FreePascal zu erziehen. Ein paar Beispiele finden Sie weiter unten.
 +
 +
Wichtig: Sie müssen den vollständigen Pfad zum ausführbaren Programm angeben. Zum Beispiel '/bin/cp' anstelle von of 'cp'. Wenn das Programm im Vorgabepfad steht, dann können Sie die Funktion [[doc:lcl/fileutil/finddefaultexecutablepath.html|FindDefaultExecutablePath]] aus der Unit [[doc:lcl/fileutil/index.html|FileUtil]] der LCL dazu benutzen.
  
 
=== Ein einfaches Beispiel ===
 
=== Ein einfaches Beispiel ===
<delphi>
+
<delphi> // Dies ist ein Demoprogramm, das zeigt, wie man ein
// Dies ist ein Demoprogramm, das zeigt, wie man ein
 
 
  // externes Programm startet.
 
  // externes Programm startet.
 
  program launchprogram;
 
  program launchprogram;
Line 52: Line 53:
 
   
 
   
 
   // Lassen sie uns den FreePascal Compiler verwenden.
 
   // Lassen sie uns den FreePascal Compiler verwenden.
   // Dazu müssen wir AProcess den Komandozeilenbefel
+
   // Dazu müssen wir den Kommandozeilenbefehl an AProcess  
 
   // übergeben:
 
   // übergeben:
 
   AProcess.CommandLine := 'ppc386 -h';
 
   AProcess.CommandLine := 'ppc386 -h';
Line 68: Line 69:
 
   // ist:
 
   // ist:
 
   AProcess.Free;   
 
   AProcess.Free;   
  end.
+
  end.</delphi>
</delphi>
 
  
Das war's. Sie haben gerade gelernt, wie man in Ihrem Programm ein externes Programm aufruft.
+
Das war's. Sie haben gerade gelernt, wie Sie in Ihrem Programm ein externes Programm aufrufen.
  
 
=== Ein verbessertes Beispiel ===
 
=== Ein verbessertes Beispiel ===
Line 78: Line 78:
 
Lassen sie uns unser Beispiel ein wenig erweitern und einfach das tun:
 
Lassen sie uns unser Beispiel ein wenig erweitern und einfach das tun:
  
<delphi>
+
<delphi> // Dies ist ein Demoprogramm, das zeigt, wie man ein externes
// Dies ist ein Demoprogramm, dass zeigt, wie man ein externes
+
  // Programm startet und dessen Output einliest.
  // Programm startet und dessen Outout einliest.
 
 
  program launchprogram;
 
  program launchprogram;
 
   
 
   
Line 90: Line 89:
 
  // Dies definiert die Variable "AProcess" als eine  
 
  // Dies definiert die Variable "AProcess" als eine  
 
  // Variable vom Typ "TProcess"
 
  // Variable vom Typ "TProcess"
  // Jetzt fügen wir auch eine TStringList hinzu, um die Ausgabe
+
  // Jetzt fügen wir auch eine TStringList hinzu, um die vom
  // des Programmes zu speichern
+
  // Programm ausgegebenen Daten zu speichern
 
 
 
  var  
 
  var  
 
   AProcess: TProcess;
 
   AProcess: TProcess;
 
   AStringList: TStringList;
 
   AStringList: TStringList;
   
+
 
 +
  // Ab hier startet Ihr Programm
 
  begin
 
  begin
 
   // Jetzt erzeugen wir das TProcess Objekt und
 
   // Jetzt erzeugen wir das TProcess Objekt und
Line 132: Line 131:
 
   AStringList.Free;
 
   AStringList.Free;
 
   AProcess.Free;   
 
   AProcess.Free;   
  end.
+
  end.</delphi>
</delphi>
 
  
 
=== Einlesen eines großen Outputs ===
 
=== Einlesen eines großen Outputs ===
Im vorherigen Beispiel haben wir gewartet, bis das Programm beendet wurde. Dann haben wir gelesen, was das Programm in seinen output geschrieben hatte. Aber angenomme, das Programm schreibt eine Menge Daten in den output, wird die Pipe voll und das Programm angehalten, weil es wartet, bis die Pipe geleert wird. Aber das aufrufende Programm liest nicht aus der pipe, bis das aufgerufene Programm beendet wurde. Ein dead lock tritt auf.
+
Im vorherigen Beispiel haben wir gewartet, bis das Programm beendet wurde. Dann haben wir gelesen, was das Programm in seinen output geschrieben hatte. Aber angenommen, das Programm schreibt eine Menge Daten in den output, wird die Pipe voll und das Programm angehalten, weil es wartet, bis die Pipe geleert wird. Aber das aufrufende Programm liest nicht aus der pipe, bis das aufgerufene Programm beendet wurde. Ein dead lock tritt auf.
  
 
Das folgende Beispiel benutzt daher nicht poWaitOnExit, sondern liest den output, während das Programm immer noch läuft. Der output wird in einem TMemoryStream gespeichert, der später verwendet werden kann, um den output in eine TStringList zu lesen.
 
Das folgende Beispiel benutzt daher nicht poWaitOnExit, sondern liest den output, während das Programm immer noch läuft. Der output wird in einem TMemoryStream gespeichert, der später verwendet werden kann, um den output in eine TStringList zu lesen.
  
<delphi>
+
<delphi> program procoutlarge;
program procoutlarge;
 
 
  {
 
  {
 
     Copyright (c) 2004 by Marc Weustink
 
     Copyright (c) 2004 by Marc Weustink
Line 164: Line 161:
 
   
 
   
 
  begin
 
  begin
   // Wir können poWaitOnExit hier nicht nutzen, weil wir
+
   // Wir können poWaitOnExit hier nicht nutzen, weil wir die
   // die Größe des Outputs nicht kennen. In Linux ist die
+
   // Größe des Outputs nicht kennen. In Linux ist die Größe der
   // Größe der Pipe 2kB. Wenn die Ausgabe größer ist,  
+
   // Pipe 2kB. Wenn die Ausgabe größer ist, müssen die Daten
   // müsse die Daten zwischenzeitlich ausgelesen werden.
+
   // zwischenzeitlich ausgelesen werden. Dies ist nicht möglich,
  // Dies ist nicht möglich, wenn auf das Ende gewartet wird
+
  // wenn auf das Ende gewartet wird - ein Deadlock tritt auf.
  // und ein Deadlock auftritt.
 
 
   //
 
   //
 
   // Ein temporärer Memorystream wird verwendet, um den output zu puffern.
 
   // Ein temporärer Memorystream wird verwendet, um den output zu puffern.
Line 225: Line 221:
 
   P.Free;
 
   P.Free;
 
   M.Free;
 
   M.Free;
  end.
+
  end.</delphi>
</delphi>
 
  
 
=== Verwendung von input und output eines TProcess ===
 
=== Verwendung von input und output eines TProcess ===
Siehe processdemo Beispiel im [https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/examples/process Lazarus-CCR SVN].
+
Siehe das Beispiel 'processdemo' im [https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/examples/process Lazarus-CCR SVN].
  
 
=== Hinweise für die Verwendung von TProcess ===
 
=== Hinweise für die Verwendung von TProcess ===
Wenn sie ein cross-platform Programm erstellen, können sie commandline unter Verwendung der Direktiven "{$IFDEF}s" und "{$ENDIF}s" entsprechend des OS ändern.
+
Wenn Sie ein plattformübergreifendes Programm erstellen, können Sie die Befehlszeile entsprechend dem Betriebssystem ändern, unter Verwendung der Direktiven "{$IFDEF}s" und "{$ENDIF}s".
  
 
Beispiel:
 
Beispiel:
{...}
+
<delphi>{...}
  AProcess:TProcess.Create(nil)
+
AProcess:TProcess.Create(nil)
  {$IFDEF WIN32}  
+
{$IFDEF WIN32}  
  AProcess.CommandLine := 'calc.exe'; //Windows Rechner
+
AProcess.CommandLine := 'calc.exe'; //Windows Rechner
  {$ENDIF}
+
{$ENDIF}
  {$IFDEF LINUX}
+
{$IFDEF LINUX}
  AProcess.CommandLine := 'kcalc'; //KDE Rechner
+
AProcess.CommandLine := 'kcalc'; //KDE Rechner
  {$ENDIF}
+
{$ENDIF}
  AProcess.Execute; //alternativ können sie AProcess.Active:=True verwenden
+
AProcess.Execute; //alternativ können sie AProcess.Active:=True verwenden
{...}
+
{...}</delphi>
  
 
=== Beispiel von "talking" mit dem aspell Prozess ===
 
=== Beispiel von "talking" mit dem aspell Prozess ===
Line 255: Line 250:
  
 
Beide Units sind ziemlich unabhängig vom Rest der pasdoc Quellen, so dass sie als praktische Demo für die Nutzung von TProcess dienen können.
 
Beide Units sind ziemlich unabhängig vom Rest der pasdoc Quellen, so dass sie als praktische Demo für die Nutzung von TProcess dienen können.
 +
 +
=== Replacing shell operators like "| < >" ===
 +
 +
Sometimes you want to run a more complicated command that pipes it's data to another command or to a file.
 +
Something like
 +
<delphi>ShellExecute('firstcommand.exe | secondcommand.exe');</delphi>
 +
or
 +
<delphi>ShellExecute('dir > output.txt');</delphi>
 +
 +
Executing this with TProcess will not work. i.e:
 +
 +
<delphi>// this won't work
 +
Process.CommandLine := 'firstcommand.exe | secondcommand.exe';
 +
Process.Execute;</delphi>
 +
 +
==== Why using special operators to redirect output doesn't work ====
 +
TProcess is just that, it's not a shell environment, only a process. It's not two processes, it's only one.
 +
It is possible to redirect output however just the way you wanted. See the [[Executing_External_Programs#How_to_redirect_output_with_TProcess |next section]].
 +
 +
=== How to redirect output with TProcess ===
 +
 +
You can redirect the output of a command to another command by using a TProcess instance for '''each''' command.
 +
 +
Here's an example that explains how to redirect the output of one process to another. To redirect the output of a process to a file/stream see the example [[Executing_External_Programs#Reading_large_output | Reading Large Output ]]
 +
<delphi>program Project1;
 +
 
 +
uses
 +
  Classes, sysutils, process;
 +
 
 +
var
 +
  FirstProcess,
 +
  SecondProcess: TProcess;
 +
  Buffer: array[0..127] of char;
 +
  ReadCount: Integer;
 +
  ReadSize: Integer;
 +
begin
 +
  FirstProcess  := TProcess.Create(nil);
 +
  SecondProcess := TProcess.Create(nil);
 +
 +
  FirstProcess.Options  := [poUsePipes];
 +
  SecondProcess.Options := [poUsePipes,poStderrToOutPut];
 +
 
 +
  FirstProcess.CommandLine  := 'pwd';
 +
  SecondProcess.CommandLine := 'grep '+ DirectorySeparator+ ' -';   
 +
  // this would be the same as "pwd | grep / -"
 +
 
 +
  FirstProcess.Execute;
 +
  SecondProcess.Execute;
 +
 
 +
  while FirstProcess.Running or (FirstProcess.Output.NumBytesAvailable > 0) do
 +
  begin
 +
    if FirstProcess.Output.NumBytesAvailable > 0 then
 +
    begin
 +
      // make sure that we don't read more data than we have allocated
 +
      // in the buffer
 +
      ReadSize := FirstProcess.Output.NumBytesAvailable;
 +
      if ReadSize > SizeOf(Buffer) then
 +
        ReadSize := SizeOf(Buffer);
 +
      // now read the output into the buffer
 +
      ReadCount := FirstProcess.Output.Read(Buffer[0], ReadSize);
 +
      // and write the buffer to the second process
 +
      SecondProcess.Input.Write(Buffer[0], ReadCount);
 +
 
 +
      // if SecondProcess writes much data to it's Output then
 +
      // we should read that data here to prevent a deadlock
 +
      // see the previous example "Reading Large Output"
 +
    end;
 +
  end;
 +
  // Close the input on the SecondProcess
 +
  // so it finishes processing it's data
 +
  SecondProcess.CloseInput;
 +
 +
  // and wait for it to complete
 +
  // be carefull what command you run because it may not exit when
 +
  // it's input is closed and the following line may loop forever
 +
  while SecondProcess.Running do
 +
    Sleep(1);
 +
  // that's it! the rest of the program is just so the example
 +
  // is a little 'useful'
 +
 +
  // we will reuse Buffer to output the SecondProcess's
 +
  // output to *this* programs stdout
 +
  WriteLn('Grep output Start:');
 +
  ReadSize := SecondProcess.Output.NumBytesAvailable;
 +
  if ReadSize > SizeOf(Buffer) then
 +
    ReadSize := SizeOf(Buffer);
 +
  if ReadSize > 0 then
 +
  begin
 +
    ReadCount := SecondProcess.Output.Read(Buffer, ReadSize);
 +
    WriteLn(Copy(Buffer,0, ReadCount));
 +
  end
 +
  else
 +
    WriteLn('grep did not find what we searched for. ', SecondProcess.ExitStatus);
 +
  WriteLn('Grep output Finish:');
 +
 
 +
  // free our process objects
 +
  FirstProcess.Free;
 +
  SecondProcess.Free;
 +
end.</delphi>
 +
 +
That's it. Now you can redirect output from one program to another.
 +
 +
==== Notes ====
 +
This example may seem overdone since it's possible to run "complicated" commands using a shell with TProcess like:
 +
<delphi>Process.Commandline := 'sh -c "pwd | grep / -"';</delphi>
 +
 +
But our example is more crossplatform since it needs no modification to run on Windows or Linux etc. "sh" may or may not exist on your platform and is generally only available on *nix platforms. Also we have more flexibility in our example since you can read and write from/to the input, output and stderr of each process individually, which could be very advantageous for your project.

Revision as of 17:34, 5 February 2011

Deutsch (de) English (en) español (es) français (fr) italiano (it) 日本語 (ja) Nederlands (nl) polski (pl) português (pt) русский (ru) slovenčina (sk) 中文(中国大陆)‎ (zh_CN)

Einleitung

Es gibt verschiedene Wege, ein externes Programm auszuführen, aber ich will mich hier auf einen konzentrieren: TProcess.

Wenn sie bereits ShellExecute und/oder WinExec in Delphi verwenden, dann können sie damit beginnen, TProcess als Alternative in FPC/Lazarus zu verwenden (dies ist auch gültig, wenn sie Lazarus unter Linux starten, weil TProcess cross-platform ist).

Notiz: FPC/Lazarus bietet Unterstützung für ShellExecute und/oder WinExec, aber diese Unterstützung gibt es nur unter Win32. Wenn ihr Programm plattformunabhängig sein soll, dann ist die Verwendung von TProcess der beste Weg!

SysUtils.ExecuteProcess

Wenn Sie keine Pipes oder sonstige Kontrollmechanismen benötigen, kann das Ausführen externer Programme über folgenden Befehl erreicht werden:

<delphi>SysUtils.ExecuteProcess( UTF8ToSys( '/full/path/to/binary'), '' , []);</delphi>

Leider "hängt" der aufrufende Prozess resp. das aufrufende Programm, wartet also, bis das aufgerufene Programm beendet wurde. Es ist deshalb besser, den Befehl CreateProcess() zu verwenden wie er hier beschrieben ist:

http://msdn.microsoft.com/en-us/library/ms682512%28v=vs.85%29.aspx

TProcess

Man kann TProcess benutzen, um externe Programme zu starten. Vorteile von TProcess sind

  • Plattform-Unabhängigkeit
  • die Fähigkeit, von stdout zu lesen und auf stdin zu schreiben.

Anmerkung: TProcess ist kein Terminal bzw. keine Shell! Weder können Scripte direkt ausgeführt werden, noch kann die Ausgabe mit Operatoren wie "|", ">", "<", "&" etc. umgeleitet werden. Es ist möglich, die gleichen Ergebnisse mit TProcess und FreePascal zu erziehen. Ein paar Beispiele finden Sie weiter unten.

Wichtig: Sie müssen den vollständigen Pfad zum ausführbaren Programm angeben. Zum Beispiel '/bin/cp' anstelle von of 'cp'. Wenn das Programm im Vorgabepfad steht, dann können Sie die Funktion FindDefaultExecutablePath aus der Unit FileUtil der LCL dazu benutzen.

Ein einfaches Beispiel

<delphi> // Dies ist ein Demoprogramm, das zeigt, wie man ein

// externes Programm startet.
program launchprogram;

// Hier binden wir Dateien ein, die hilfreiche Funktionen
// und Prozeduren haben, die wir benötigen.
uses 
  Classes, SysUtils, Process;

// Dies definiert die Variable "AProcess" als eine Variable 
// vom Typ "TProcess"
var 
  AProcess: TProcess;

// Hier beginnt der Befehlstext:
begin
  // Nun erstellen wir das Objekt TProcess und
  // weisen es der Variable AProcess zu.
  AProcess := TProcess.Create(nil);

  // Lassen sie uns den FreePascal Compiler verwenden.
  // Dazu müssen wir den Kommandozeilenbefehl an AProcess 
  // übergeben:
  AProcess.CommandLine := 'ppc386 -h';

  // Während das externe Programm läuft, soll unser
  // Programm natürlich nicht weiterlaufen.
  // Dies regeln wir mit folgender Bedingung: 
  AProcess.Options := AProcess.Options + [poWaitOnExit];

  // Nun muss AProcess noch ausgeführt werden...
  AProcess.Execute;

  // Wegen der oben gesetzter Bedingung wird dieser
  // Codeabschnitt erst erreicht, wenn ppc386 beendet
  // ist:
  AProcess.Free;   
end.</delphi>

Das war's. Sie haben gerade gelernt, wie Sie in Ihrem Programm ein externes Programm aufrufen.

Ein verbessertes Beispiel

Das ist nett, aber wie lese ich den Output von einem Programm, das ich laufen habe?

Lassen sie uns unser Beispiel ein wenig erweitern und einfach das tun:

<delphi> // Dies ist ein Demoprogramm, das zeigt, wie man ein externes

// Programm startet und dessen Output einliest.
program launchprogram;

// Hier binden wir Dateien ein, die hilfreiche Funktionen
// und Prozeduren haben, die wir benötigen.
uses 
  Classes, SysUtils, Process;

// Dies definiert die Variable "AProcess" als eine 
// Variable vom Typ "TProcess"
// Jetzt fügen wir auch eine TStringList hinzu, um die vom
// Programm ausgegebenen Daten zu speichern
var 
  AProcess: TProcess;
  AStringList: TStringList;
// Ab hier startet Ihr Programm
begin
  // Jetzt erzeugen wir das TProcess Objekt und
  // ordnen es der Variablen AProcess zu.
  AProcess := TProcess.Create(nil);

  // Erzeugen des TStringList Objekts.
  AStringList := TStringList.Create;

  // Gibt an, welcher Befehl vom Prozess ausgeführt werden soll
  // Lassen sie uns den FreePascal Compiler verwenden
  AProcess.CommandLine := 'ppc386 -h';

  // Wir definieren eine Option, wie das Programm
  // ausgeführt werden soll. Dies stellt sicher, dass
  // unser Programm nicht vor Beendigung des aufgerufenen
  // Programmes fortgesetzt wird. Außerdem geben wir an,
  // dass wir die Ausgabe lesen wollen
  AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];

  // Startet den Prozess, nachdem die Parameter entsprechend
  // gesetzt sind
  AProcess.Execute;
  
  // Folgendes wird erst nach Beendigung von ppc386 ausgeführt

  // Die Ausgabe wird nun in die Stringliste gelesen
  AStringList.LoadFromStream(AProcess.Output);
  
  // Speichert den output in eine Datei.
  AStringList.SaveToFile('output.txt');

  // Nun da die Datei gespeichert ist können wir 
  // TStringList und TProcess freigeben.
  AStringList.Free;
  AProcess.Free;   
end.</delphi>

Einlesen eines großen Outputs

Im vorherigen Beispiel haben wir gewartet, bis das Programm beendet wurde. Dann haben wir gelesen, was das Programm in seinen output geschrieben hatte. Aber angenommen, das Programm schreibt eine Menge Daten in den output, wird die Pipe voll und das Programm angehalten, weil es wartet, bis die Pipe geleert wird. Aber das aufrufende Programm liest nicht aus der pipe, bis das aufgerufene Programm beendet wurde. Ein dead lock tritt auf.

Das folgende Beispiel benutzt daher nicht poWaitOnExit, sondern liest den output, während das Programm immer noch läuft. Der output wird in einem TMemoryStream gespeichert, der später verwendet werden kann, um den output in eine TStringList zu lesen.

<delphi> program procoutlarge;

{
    Copyright (c) 2004 by Marc Weustink

    This example is creeated in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
}

uses
  Classes, Process, SysUtils;

const
  READ_BYTES = 2048;
  
var
  S: TStringList;
  M: TMemoryStream;
  P: TProcess;
  n: LongInt;
  BytesRead: LongInt;

begin
  // Wir können poWaitOnExit hier nicht nutzen, weil wir die
  // Größe des Outputs nicht kennen. In Linux ist die Größe der
  // Pipe 2kB. Wenn die Ausgabe größer ist, müssen die Daten 
  // zwischenzeitlich ausgelesen werden. Dies ist nicht möglich,
  // wenn auf das Ende gewartet wird - ein Deadlock tritt auf.
  //
  // Ein temporärer Memorystream wird verwendet, um den output zu puffern.
  
  M := TMemoryStream.Create;
  BytesRead := 0;

  P := TProcess.Create(nil);
  P.CommandLine := 'ppc386 -va bogus.pp';
  P.Options := [poUsePipes];
  WriteLn('-- executing --');
  P.Execute;
  while P.Running do
  begin          
    // stellt sicher, dass wir Platz haben
    M.SetSize(BytesRead + READ_BYTES);
    
    // versuche, es zu lesen
    n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
    if n > 0 
    then begin
      Inc(BytesRead, n);
      Write('.')
    end
    else begin     
      // keine Daten, warte 100 ms
      Sleep(100); 
    end;
  end;
  // lese den letzten Teil
  repeat
    // stellt sicher, dass wir Platz haben
    M.SetSize(BytesRead + READ_BYTES);
    // versuche es zu lesen
    n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
    if n > 0 
    then begin
      Inc(BytesRead, n);
      Write('.');
    end;
  until n <= 0;
  if BytesRead > 0 then WriteLn;
  M.SetSize(BytesRead); 
  WriteLn('-- executed --');
  
  S := TStringList.Create;
  S.LoadFromStream(M);
  WriteLn('-- linecount = ', S.Count, ' --');
  for n := 0 to S.Count - 1 do
  begin
    WriteLn('| ', S[n]);
  end;
  WriteLn('-- end --');
  S.Free;
  P.Free;
  M.Free;
end.</delphi>

Verwendung von input und output eines TProcess

Siehe das Beispiel 'processdemo' im Lazarus-CCR SVN.

Hinweise für die Verwendung von TProcess

Wenn Sie ein plattformübergreifendes Programm erstellen, können Sie die Befehlszeile entsprechend dem Betriebssystem ändern, unter Verwendung der Direktiven "{$IFDEF}s" und "{$ENDIF}s".

Beispiel: <delphi>{...} AProcess:TProcess.Create(nil) {$IFDEF WIN32} AProcess.CommandLine := 'calc.exe'; //Windows Rechner {$ENDIF} {$IFDEF LINUX} AProcess.CommandLine := 'kcalc'; //KDE Rechner {$ENDIF} AProcess.Execute; //alternativ können sie AProcess.Active:=True verwenden {...}</delphi>

Beispiel von "talking" mit dem aspell Prozess

Innerhalb des pasdoc Quellcodes können sie zwei Units finden, die die Rechtschreibung prüfen, in dem sie mit dem laufenden aspell-Prozess durch Pipes kommuniziert:

  • PasDoc_ProcessLineTalk.pas unit implementiert die TProcessLineTalk Klasse, einen Nachfahren von TProcess, der einfach benutzt werden kann, um eine "Zeilenweise" Kommunikation mit einem laufenden Prozess zu ermöglichen.
  • PasDoc_Aspell.pas units implementiert die TAspellProcess Klasse, welche die Rechtschreibprüfung ausführt, in dem es den darunter liegenden Prozess von TProcessLineTalk nutzt, um aspell auszuführen und mit dem laufenden Prozess zu kommunizieren.

Beide Units sind ziemlich unabhängig vom Rest der pasdoc Quellen, so dass sie als praktische Demo für die Nutzung von TProcess dienen können.

Replacing shell operators like "| < >"

Sometimes you want to run a more complicated command that pipes it's data to another command or to a file. Something like <delphi>ShellExecute('firstcommand.exe | secondcommand.exe');</delphi> or <delphi>ShellExecute('dir > output.txt');</delphi>

Executing this with TProcess will not work. i.e:

<delphi>// this won't work Process.CommandLine := 'firstcommand.exe | secondcommand.exe'; Process.Execute;</delphi>

Why using special operators to redirect output doesn't work

TProcess is just that, it's not a shell environment, only a process. It's not two processes, it's only one. It is possible to redirect output however just the way you wanted. See the next section.

How to redirect output with TProcess

You can redirect the output of a command to another command by using a TProcess instance for each command.

Here's an example that explains how to redirect the output of one process to another. To redirect the output of a process to a file/stream see the example Reading Large Output <delphi>program Project1;

uses

 Classes, sysutils, process;
 

var

 FirstProcess,
 SecondProcess: TProcess;
 Buffer: array[0..127] of char;
 ReadCount: Integer;
 ReadSize: Integer;

begin

 FirstProcess  := TProcess.Create(nil);
 SecondProcess := TProcess.Create(nil);

 FirstProcess.Options  := [poUsePipes];
 SecondProcess.Options := [poUsePipes,poStderrToOutPut];
 
 FirstProcess.CommandLine  := 'pwd';
 SecondProcess.CommandLine := 'grep '+ DirectorySeparator+ ' -';    
 // this would be the same as "pwd | grep / -"
 
 FirstProcess.Execute;
 SecondProcess.Execute;
 
 while FirstProcess.Running or (FirstProcess.Output.NumBytesAvailable > 0) do
 begin
   if FirstProcess.Output.NumBytesAvailable > 0 then
   begin
     // make sure that we don't read more data than we have allocated
     // in the buffer
     ReadSize := FirstProcess.Output.NumBytesAvailable;
     if ReadSize > SizeOf(Buffer) then
       ReadSize := SizeOf(Buffer);
     // now read the output into the buffer
     ReadCount := FirstProcess.Output.Read(Buffer[0], ReadSize);
     // and write the buffer to the second process
     SecondProcess.Input.Write(Buffer[0], ReadCount);
 
     // if SecondProcess writes much data to it's Output then 
     // we should read that data here to prevent a deadlock
     // see the previous example "Reading Large Output"
   end;
 end;
 // Close the input on the SecondProcess
 // so it finishes processing it's data
 SecondProcess.CloseInput;

 // and wait for it to complete
 // be carefull what command you run because it may not exit when
 // it's input is closed and the following line may loop forever
 while SecondProcess.Running do
   Sleep(1);
 // that's it! the rest of the program is just so the example
 // is a little 'useful'
 // we will reuse Buffer to output the SecondProcess's
 // output to *this* programs stdout
 WriteLn('Grep output Start:');
 ReadSize := SecondProcess.Output.NumBytesAvailable;
 if ReadSize > SizeOf(Buffer) then
   ReadSize := SizeOf(Buffer);
 if ReadSize > 0 then
 begin
   ReadCount := SecondProcess.Output.Read(Buffer, ReadSize);
   WriteLn(Copy(Buffer,0, ReadCount));
 end
 else
   WriteLn('grep did not find what we searched for. ', SecondProcess.ExitStatus);
 WriteLn('Grep output Finish:');
 
 // free our process objects
 FirstProcess.Free;
 SecondProcess.Free;

end.</delphi>

That's it. Now you can redirect output from one program to another.

Notes

This example may seem overdone since it's possible to run "complicated" commands using a shell with TProcess like: <delphi>Process.Commandline := 'sh -c "pwd | grep / -"';</delphi>

But our example is more crossplatform since it needs no modification to run on Windows or Linux etc. "sh" may or may not exist on your platform and is generally only available on *nix platforms. Also we have more flexibility in our example since you can read and write from/to the input, output and stderr of each process individually, which could be very advantageous for your project.