Sign in Join
Wiki copytemporary

Executing External Programs/de: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Executing External Programs/de

OldNewLine
1+Zurück zu den [[Additional information/de|Zusätzlichen Informationen]].
2+
3+## Einleitung
4+
5+Es gibt verschiedene Wege, ein externes Programm auszuführen, aber ich will mich hier auf einen konzentrieren: [[doc:fcl/process/tprocess.html|TProcess]].
6+
7+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 plattformübergreifend ist).
8+
9+> **Note:** Die englische Variante dieser Seite ist teilweise ausführlicher(+: MSWindows: CreateProcess, ShellExecute and WinExec + Unix fpsystem, fpexecve and shell + macOS + Run detached program und auch danach noch einiges mehr)
10+
11+> **Note:** 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!
12+
13+## Überblick : Vergleich
14+
15+| Unit | Methode | Plattformen | Befehlszeile | Features und Anmerkungen |
16+|----|----|----|----|----|
17+| SysUtils | [ExecuteProcess](https://www.freepascal.org/docs-html/rtl/sysutils/executeprocess.html) | Cross-Platform | Ja | Sehr limitiert, synchron |
18+| ShellApi | [ShellExecute](https://msdn.microsoft.com/en-us/library/windows/desktop/bb762153%28v=vs.85%29.aspx) | Nur MS Windows | Ja | Viele. Kann Programme mit Admin Rechten starten. |
19+| | [WinExec](https://msdn.microsoft.com/en-us/library/windows/desktop/ms687393%28v=vs.85%29.aspx) | Nur MS Windows | Ja | Kompatibilität mit 16-bit-Anwendungen |
20+| | [CreateProcess](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx) | Nur MS Windows | Ja | Voll |
21+| Unix | [fpsystem](https://www.freepascal.org/docs-html/rtl/unix/fpsystem.html) | Nur Unix | Ja | Ruft die Systemshell `/bin/sh` auf. |
22+| BaseUnix | [fpexecve](https://www.freepascal.org/docs-html/rtl/baseunix/fpexecve.html) | Nur Unix | Nein | Vollständige Kontrolle über Parameter und Umgebungsvariablen. Aktuelles Programm wird nicht weiter ausgeführt. |
23+| Process | [TProcess](https://www.freepascal.org/docs-html/fcl/process/tprocess.html) | Cross-Platform | Nein | Voll |
24+| Process | [RunCommand](https://www.freepascal.org/docs-html/fcl/process/runcommand.html), [RunCommandInDir](https://www.freepascal.org/docs-html/fcl/process/runcommandindir.html) | Cross-Platform **Benötigt FPC 2.6.2+** | Ja | Wie TProcess |
25+| LCLIntf | [OpenDocument](https://lazarus-ccr.sourceforge.io/docs/lcl/lclintf/opendocument.html) | Cross-Platform | Ja | Nur Dokument öffnen |
26+| LCLIntf | [OpenURL](https://lazarus-ccr.sourceforge.io/docs/lcl/lclintf/openurl.html) | Cross-Platform | Ja | URL öffnen |
27+
28+## (Process.)RunCommand
29+
30+- [RunCommand](http://www.freepascal.org/docs-html/fcl/process/runcommand.html) siehe dort
31+- [RunCommandInDir](http://www.freepascal.org/docs-html/fcl/process/runcommandindir.html) siehe dort
32+
33+Mit FPC 2.6.2 wurden für TProcess einige Hilfsfunktionen zur Unit Process hinzugefügt basierend auf Wrapper im [[Projects using Lazarus|fpcup]]-Projekt.
34+
35+Diese Funktionen sind grundlegend und sofort benutzbar, sowohl wenn als Ergebnis des Kommandos ein einfacher String als auch wenn ein längeres Ergebnis zu erwarten ist.
36+
37+Ein einfaches Beispiel:
38+
39+``` pascal
40+program project1;
41+
42+{$mode objfpc}{$H+}
43+
44+uses
45+ Process;
46+
47+var
48+ s : ansistring;
49+
50+begin
51+
52+if RunCommand('/bin/bash',['-c','echo $PATH'],s) then
53+ writeln(s);
54+
55+end.
56+```
57+
58+Aber bitte beachten Sie, dass nicht alle 'built-in' Aufrufe (z. B. in Linux Alias) funktionieren,\
59+1.) weil Alias-Werte nur in interaktiven Shells standardmäßig expandiert werden und\
60+2.) weil auch .bashrc nur in interaktiven Shells gelesen wird, es sei denn die BASH_ENV Environment Variable ist gesetzt.\
61+So ergibt z. B. folgendes Programm keinen Output.
62+
63+``` pascal
64+program project2;
65+
66+{$mode objfpc}{$H+}
67+
68+uses
69+ Process;
70+
71+var
72+ s : ansistring;
73+
74+begin
75+
76+if RunCommand('/bin/bash',['-c','alias'],s) then
77+ writeln(s);
78+
79+end.
80+```
81+
82+Eine überladene Variante von RunCommand liefert zusätzlich den Exitcode des Programs. RunCommandInDir setzt noch zusätzlich das Standardverzeichnis (setzt p.CurrentDirectory):
83+
84+``` pascal
85+function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string; var exitstatus:integer): integer;
86+function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string): boolean;
87+function RunCommand(const exename:string;const commands:array of string;var outputstring:string): boolean;
88+```
89+
90+## SysUtils.ExecuteProcess
91+
92+Wenn Sie keine Pipes oder sonstige Kontrollmechanismen benötigen, kann das Ausführen externer Programme über folgenden Befehl erreicht werden:
93+
94+``` pascal
95+SysUtils.ExecuteProcess( UTF8ToSys( '/full/path/to/binary'), '''' '', []);
96+```
97+
98+Leider wird der aufrufende Prozess synchron ausgeführt: er "hängt", wartet also, bis das aufgerufene Programm beendet wurde. Es ist deshalb besser, den Befehl CreateProcess() zu verwenden, wie es hier beschrieben ist:
99+
100+[`http://msdn.microsoft.com/en-us/library/ms682512%28v=vs.85%29.aspx`](http://msdn.microsoft.com/en-us/library/ms682512%28v=vs.85%29.aspx)
101+
102+## TProcess
103+
104+Man kann TProcess benutzen, um externe Programme zu starten. Vorteile von TProcess sind
105+
106+- Plattform-Unabhängigkeit
107+- die Fähigkeit, von *stdout* zu lesen und auf *stdin* zu schreiben.
108+
109+Anmerkung: TProcess ist kein Terminal bzw. keine Shell! Weder können Skripte 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 erzielen. Ein paar Beispiele finden Sie weiter unten.
110+
111+Wichtig: Sie müssen den vollständigen Pfad zum ausführbaren Programm angeben. Zum Beispiel '/bin/cp' anstelle von '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.
112+
113+### Ein einfaches Beispiel
114+
115+``` pascal
116+// Dies ist ein Demoprogramm, das zeigt, wie man ein
117+// externes Programm startet.
118+program launchprogram;
119+
120+// Hier binden wir Dateien ein, die hilfreiche Funktionen
121+// und Prozeduren haben, die wir benötigen.
122+uses
123+ Classes, SysUtils, Process;
124+
125+// Dies definiert die Variable "AProcess" als eine Variable
126+// vom Typ "TProcess"
127+var
128+ AProcess: TProcess;
129+
130+// Hier beginnt der Befehlstext:
131+begin
132+ // Nun erstellen wir das Objekt TProcess und
133+ // weisen es der Variable AProcess zu.
134+ AProcess := TProcess.Create(nil);
135+
136+ // Lassen sie uns den FreePascal Compiler verwenden.
137+ // Dazu müssen wir den Programmnamen an AProcess
138+ // übergeben:
139+ AProcess.Executable:= 'ppc386';
140+
141+ // An das Programm soll der Parameter -h übergeben werden:
142+ AProcess.Parameters.Add('-h');
143+
144+ // Während das externe Programm läuft, soll unser
145+ // Programm natürlich nicht weiterlaufen.
146+ // Dies regeln wir mit folgender Bedingung:
147+ AProcess.Options := AProcess.Options + [poWaitOnExit];
148+
149+ // Nun muss AProcess noch ausgeführt werden...
150+ AProcess.Execute;
151+
152+ // Wegen der oben gesetzter Bedingung wird dieser
153+ // Codeabschnitt erst erreicht, wenn ppc386 beendet
154+ // ist:
155+ AProcess.Free;
156+end.
157+```
158+
159+Das war's. Sie haben gerade gelernt, wie Sie in Ihrem Programm ein externes Programm aufrufen.
160+
161+### Ein verbessertes Beispiel
162+
163+Das ist nett, aber wie lese ich den Output von einem Programm, das ich laufen habe?
164+
165+Lassen Sie uns unser Beispiel ein wenig erweitern und einfach das tun:
166+
167+``` pascal
168+// Dies ist ein Demoprogramm, das zeigt, wie man ein externes
169+// Programm startet und dessen Output einliest.
170+program launchprogram;
171+
172+// Hier binden wir Dateien ein, die hilfreiche Funktionen
173+// und Prozeduren haben, die wir benötigen.
174+uses
175+ Classes, SysUtils, Process;
176+
177+// Dies definiert die Variable "AProcess" als eine
178+// Variable vom Typ "TProcess"
179+// Jetzt fügen wir auch eine TStringList hinzu, um die vom
180+// Programm ausgegebenen Daten zu speichern
181+var
182+ AProcess: TProcess;
183+ AStringList: TStringList;
184+
185+// Ab hier startet Ihr Programm
186+begin
187+ // Jetzt erzeugen wir das TProcess Objekt und
188+ // ordnen es der Variablen AProcess zu.
189+ AProcess := TProcess.Create(nil);
190+
191+ // Erzeugen des TStringList Objekts.
192+ AStringList := TStringList.Create;
193+
194+ // Gibt an, welcher Befehl vom Prozess ausgeführt werden soll
195+ // Lassen sie uns den FreePascal Compiler verwenden
196+ AProcess.CommandLine := 'ppc386 -h';
197+
198+ // Wir definieren eine Option, wie das Programm
199+ // ausgeführt werden soll. Dies stellt sicher, dass
200+ // unser Programm nicht vor Beendigung des aufgerufenen
201+ // Programmes fortgesetzt wird. Außerdem geben wir an,
202+ // dass wir die Ausgabe lesen wollen
203+ AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
204+
205+ // Startet den Prozess, nachdem die Parameter entsprechend
206+ // gesetzt sind
207+ AProcess.Execute;
208+
209+ // Folgendes wird erst nach Beendigung von ppc386 ausgeführt
210+
211+ // Die Ausgabe wird nun in die Stringliste gelesen
212+ AStringList.LoadFromStream(AProcess.Output);
213+
214+ // Speichert den output in eine Datei.
215+ AStringList.SaveToFile('output.txt');
216+
217+ // Nun da die Datei gespeichert ist können wir
218+ // TStringList und TProcess freigeben.
219+ AStringList.Free;
220+ AProcess.Free;
221+end.
222+```
223+
224+### Einlesen eines großen Outputs
225+
226+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.
227+
228+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 einzulesen.
229+
230+``` pascal
231+program procoutlarge;
232+{
233+ Copyright (c) 2004 by Marc Weustink
234+
235+ This example is creeated in the hope that it will be useful,
236+ but WITHOUT ANY WARRANTY; without even the implied warranty of
237+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
238+}
239+
240+uses
241+ Classes, Process, SysUtils;
242+
243+const
244+ READ_BYTES = 2048;
245+
246+var
247+ S: TStringList;
248+ M: TMemoryStream;
249+ P: TProcess;
250+ n: LongInt;
251+ BytesRead: LongInt;
252+
253+begin
254+ // Wir können poWaitOnExit hier nicht nutzen, weil wir die
255+ // Größe des Outputs nicht kennen. In Linux ist die Größe der
256+ // Pipe 2kB. Wenn die Ausgabe größer ist, müssen die Daten
257+ // zwischenzeitlich ausgelesen werden. Dies ist nicht möglich,
258+ // wenn auf das Ende gewartet wird - ein Deadlock tritt auf.
259+ //
260+ // Ein temporärer Memorystream wird verwendet, um den output zu puffern.
261+
262+ M := TMemoryStream.Create;
263+ BytesRead := 0;
264+
265+ P := TProcess.Create(nil);
266+ P.CommandLine := 'ppc386 -va bogus.pp';
267+ P.Options := [poUsePipes];
268+ WriteLn('-- executing --');
269+ P.Execute;
270+ while P.Running do
271+ begin
272+ // stellt sicher, dass wir Platz haben
273+ M.SetSize(BytesRead + READ_BYTES);
274+
275+ // versuche, es zu lesen
276+ n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
277+ if n > 0
278+ then begin
279+ Inc(BytesRead, n);
280+ Write('.')
281+ end
282+ else begin
283+ // keine Daten, warte 100 ms
284+ Sleep(100);
285+ end;
286+ end;
287+ // lese den letzten Teil
288+ repeat
289+ // stellt sicher, dass wir Platz haben
290+ M.SetSize(BytesRead + READ_BYTES);
291+ // versuche es zu lesen
292+ n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
293+ if n > 0
294+ then begin
295+ Inc(BytesRead, n);
296+ Write('.');
297+ end;
298+ until n <= 0;
299+ if BytesRead > 0 then WriteLn;
300+ M.SetSize(BytesRead);
301+ WriteLn('-- executed --');
302+
303+ S := TStringList.Create;
304+ S.LoadFromStream(M);
305+ WriteLn('-- linecount = ', S.Count, ' --');
306+ for n := 0 to S.Count - 1 do
307+ begin
308+ WriteLn('| ', S[n]);
309+ end;
310+ WriteLn('-- end --');
311+ S.Free;
312+ P.Free;
313+ M.Free;
314+end.
315+```
316+
317+### Verwendung von input und output eines TProcess
318+
319+Siehe das Beispiel 'processdemo' im [Lazarus-CCR SVN](https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/examples/process).
320+
321+### Hinweise für die Verwendung von TProcess
322+
323+Mit den Direktiven `{$IFDEF}` und `{$ENDIF}` können Sie das Programm je nach Betriebssystem auswählen.
324+
325+``` pascal
326+AProcess:TProcess.Create(nil)
327+{$IFDEF WIN32}
328+AProcess.Executable := 'calc.exe'; // Windows Rechner
329+{$ENDIF}
330+{$IFDEF LINUX}
331+AProcess.Executable := 'kcalc'; // KDE Rechner
332+{$ENDIF}
333+AProcess.Execute; // alternativ können sie AProcess.Active := True verwenden
334+```
335+
336+### Beispiel von "talking" mit dem aspell Prozess
337+
338+Innerhalb des [pasdoc](http://pasdoc.sourceforge.net/) Quellcodes können Sie zwei Units finden, die die Rechtschreibung prüfen, in dem sie mit dem laufenden aspell-Prozess durch Pipes kommunizieren:
339+
340+- [PasDoc_ProcessLineTalk.pas unit](http://pasdoc.svn.sourceforge.net/viewvc/*checkout*/pasdoc/trunk/source/component/PasDoc_ProcessLineTalk.pas) implementiert die Klasse *TProcessLineTalk*, einen Nachfahren von TProcess, der einfach benutzt werden kann, um eine "zeilenweise" Kommunikation mit einem laufenden Prozess zu ermöglichen.
341+
342+- [PasDoc_Aspell.pas units](http://pasdoc.svn.sourceforge.net/viewvc/*checkout*/pasdoc/trunk/source/component/PasDoc_Aspell.pas) implementiert die Klasse *TAspellProcess*, welche die Rechtschreibprüfung ausführt, indem sie den darunter liegenden Prozess von TProcessLineTalk nutzt, um aspell auszuführen und mit dem laufenden Prozess zu kommunizieren.
343+
344+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.
345+
346+### Ersetzen von Shell-Operatoren wie "\| \< \>"
347+
348+Gelegentlich möchten Sie einen komplizierteren Befehl starten, der seine Daten an einen anderen Befehl oder eine Datei weiterleitet. Also etwa folgendes
349+
350+``` pascal
351+ShellExecute('ersterBefehl.exe | zweiterBefehl.exe');
352+```
353+
354+oder
355+
356+``` pascal
357+ShellExecute('dir > output.txt');
358+```
359+
360+Das mit TProcess auszuführen klappt nicht, also:
361+
362+``` pascal
363+// das klappt nicht
364+Process.CommandLine := 'ersterBefehl.exe | zweiterBefehl.exe';
365+Process.Execute;
366+```
367+
368+#### Warum die Verwendung von Spezialoperatoren zum Umleiten der Ausgabe nicht funktioniert
369+
370+TProcess ist keine Shell-Umgebung, sondern nur ein Prozess. Also nicht zwei Prozesse, nur einer. Dennoch ist die Umleitung der Ausgabe in der gewünschten Weise möglich. Siehe dazu den nächsten Abschnitt [[Executing External Programs/de|Wie leitet man die Ausgabe mit TProcess um]].
371+
372+### Wie leitet man die Ausgabe mit TProcess um
373+
374+Sie können die Ausgaben eines Befehls an einen anderen Befehl umleiten, indem Sie für **jeden** Befehl eine Instanz des TProcess verwenden.
375+
376+Hier ist ein Beispiel das die Umleitung von einem Prozess an einen anderen erläutert. Für die Umleitung der Ausgabe von einem Prozess zu einer Datei/einem Stream siehe das Beispiel [[Executing External Programs/de|Einlesen eines großen Outputs]]
377+
378+Sie können nicht nur die "normale" Ausgabe (auch als "stdout" bekannt) umleiten, sondern auch die Fehlerausgabe (stderr), wenn Sie die Option "poStderrToOutPut" angeben, wie in den Optionen des zweiten Prozesses ersichtlich.
379+
380+``` pascal
381+program Project1;
382+
383+uses
384+ Classes, sysutils, process;
385+
386+var
387+ FirstProcess,
388+ SecondProcess: TProcess;
389+ Buffer: array[0..127] of char;
390+ ReadCount: Integer;
391+ ReadSize: Integer;
392+begin
393+ FirstProcess := TProcess.Create(nil);
394+ SecondProcess := TProcess.Create(nil);
395+
396+ FirstProcess.Options := [poUsePipes];
397+ SecondProcess.Options := [poUsePipes,poStderrToOutPut];
398+
399+ FirstProcess.CommandLine := 'pwd';
400+ SecondProcess.CommandLine := 'grep '+ DirectorySeparator+ ' -';
401+ // dies liefert das gleiche wie "pwd | grep / -"
402+
403+ FirstProcess.Execute;
404+ SecondProcess.Execute;
405+
406+ while FirstProcess.Running or (FirstProcess.Output.NumBytesAvailable > 0) do
407+ begin
408+ if FirstProcess.Output.NumBytesAvailable > 0 then
409+ begin
410+ // stellen Sie sicher, dass Sie nicht mehr Daten lesen als im Puffer
411+ // bereitstehen
412+ ReadSize := FirstProcess.Output.NumBytesAvailable;
413+ if ReadSize > SizeOf(Buffer) then
414+ ReadSize := SizeOf(Buffer);
415+ // jetzt lesen wir die Ausgabe in den Puffer
416+ ReadCount := FirstProcess.Output.Read(Buffer[0], ReadSize);
417+ // und schreiben den Puffer an den zweiten Prozess
418+ SecondProcess.Input.Write(Buffer[0], ReadCount);
419+
420+ // falls der SecondProcess große Datenmengen an seine Ausgabe sendet, dann
421+ // sollten wir diese Daten auslesen, um einen Deadlock zu vermeiden.
422+ // siehe das vorhergehende Beispiel "Einlesen eines großen Outputs"
423+ end;
424+ end;
425+ // Schließt die Eingabe an den SecondProcess,
426+ // sodass der die Verarbeitung seiner Daten beendet
427+ SecondProcess.CloseInput;
428+
429+ // und warte auf seine Beendigung.
430+ // Sei achtsam, welcher Befehl ausgeführt werden soll, weil er nicht beendet würde,
431+ // wenn seine Eingabe geschlossen ist. Die folgende Zeile führt dann zu einer unendlichen Schleife
432+ while SecondProcess.Running do
433+ Sleep(1);
434+ // Das wars! Der Rest des Programms ist nur dazu da, dass das Beispiel
435+ // irgendwas 'nützliches' macht.
436+
437+ // Wir benutzen wieder den Puffer, diesmal für die Ausgabe vom SecondProcess
438+ // an die Standardausgabe stdout dieses Beispielprogrammes.
439+ WriteLn('Grep output Start:');
440+ ReadSize := SecondProcess.Output.NumBytesAvailable;
441+ if ReadSize > SizeOf(Buffer) then
442+ ReadSize := SizeOf(Buffer);
443+ if ReadSize > 0 then
444+ begin
445+ ReadCount := SecondProcess.Output.Read(Buffer, ReadSize);
446+ WriteLn(Copy(Buffer,0, ReadCount));
447+ end
448+ else
449+ WriteLn('grep did not find what we searched for. ', SecondProcess.ExitStatus);
450+ WriteLn('Grep output Finish:');
451+
452+ // free our process objects
453+ FirstProcess.Free;
454+ SecondProcess.Free;
455+end.
456+```
457+
458+Das war es. Nun können Sie Ausgaben von einem Programm an das nächste umleiten.
459+
460+#### Anmerkungen
461+
462+Dieses Beipiel könnte übertrieben erscheinen, weil es ermöglicht, mit TProcess auch "komplizierte" Befehle unter Verwendung einer Shell zu starten, wie:
463+
464+``` pascal
465+Process.Commandline := 'sh -c "pwd | grep / -"';
466+```
467+
468+Aber unser Beispiel ist dadurch wirklich plattformübergreifend. Es braucht keinerlei Modifikation um unter Windows oder Linux etc. ausgeführt zu werden. Der Befehl "sh" ist unter Ihrem Betriebssystem möglicherweise nicht verfügbar, im Allgemeinen gibt es ihn nur auf *nix Systemen.\
469+Ebenso haben wir in unserem Beispiel mehr Flexibilität gewonnen, da wir zu und von der Eingabe, der Ausgabe und zu 'stderr' lesen und schreiben können, und das individuell für jeden Prozess. Das könnte in Ihrem Projekt sehr vorteilhaft sein.
470+
471+### Parameter, die Leerzeichen enthalten (Replacing Shell Quotes)
472+
473+In der Linux-Shell ist es möglich, Argumente in Anführungszeichen zu schreiben, ähnlich dem Folgenden:
474+
475+`gdb --batch --eval-command="info symbol 0x0000DDDD" myprogram`
476+
477+Und GDB erhält dadurch 3 Argumente (zusätzlich zum ersten Argument, das den vollständigen Pfad zum ausführbaren Programm enthält):
478+
479+1. --batch
480+2. --eval-command=info symbol 0x0000DDDD
481+3. the full path to myprogram
482+
483+TProcess und auch (???) übergeben Parameter mit Leerzeichen, aber sie benutzen einen unterschiedlichen Stil dazu. Anstatt nur Teile des Parameters in Anführungszeichen zu setzen, schließen sie alles ein. Ähnlich wie:
484+
485+``` pascal
486+ TProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /home/me/myprogram';
487+```
488+
489+Und beachten Sie auch, dass Sie nur vollständige Pfade übergeben.
490+
491+Siehe dazu auch die Diskussion darüber unter: <http://bugs.freepascal.org/view.php?id=14446>
492+
493+### RunCommand Erweiterungen
494+
495+Im FPC 3.2.0+ wurde die RunCommand implementation verbessert und in die TProcess Klasse eingefügt. Dies erlaubt eine schnellere Erstellung eigener Speziallisierungen der TProcess Klasse. Hier ein Beispiel mit Timeout:
496+
497+``` pascal
498+program TestTProcessTimeout;
499+
500+uses classes, sysutils, process, dateutils;
501+
502+type
503+ { TProcessTimeout }
504+ TProcessTimeout = class(TProcess)
505+ protected
506+ timeoutperiod: TTime;
507+ timedout : boolean;
508+ started : TDateTime;
509+ procedure LocalnIdleSleep(Sender,Context : TObject;status:TRunCommandEventCode;const message:string);
510+ public
511+ class function RunCommandwithTimeout(const exename:TProcessString;const commands:array of TProcessString;const dir:string;out outputstring:string;out errorstring:string;out exitstate:integer; ProcessOptions : TProcessOptions = [];SWOptions:TShowWindowOptions=swoNone;timeout:integer=15):boolean;static;
512+ end;
513+
514+procedure TProcessTimeout.LocalnIdleSleep(Sender,Context : TObject;status:TRunCommandEventCode;const message:string);
515+begin
516+ if status=RunCommandIdle then
517+ begin
518+ writeln(Executable+'('+ Parameters.CommaText+')' +' time: '+TimeToStr(now-started));
519+ if (now-started)>timeoutperiod then
520+ begin
521+ writeln('process timed out ');
522+ timedout:=true;
523+ Terminate(255);
524+ exit;
525+ end;
526+ sleep(RunCommandSleepTime);
527+ end;
528+end;
529+
530+class function TProcessTimeout.RunCommandwithTimeout(
531+ const exename : TProcessString;
532+ const commands : array of TProcessString;
533+ const dir : string;
534+ out outputstring : string;
535+ out errorstring : string;
536+ out exitstate : integer;
537+ ProcessOptions : TProcessOptions = [];
538+ SWOptions : TShowWindowOptions=swoNone;
539+ timeout : integer=15
540+ ):boolean;
541+Var
542+ p : TProcessTimeout;
543+ i : integer;
544+begin
545+ p:=TProcessTimeout.create(nil);
546+ p.OnRunCommandEvent:=@p.LocalnIdleSleep;
547+ p.CurrentDirectory:=dir;
548+
549+ //timeout in Minuten
550+ p.timeoutperiod:=timeout/MinsPerDay;
551+
552+ (*
553+ //Wenn der Timeout in sekunden angegeben werden soll dies hier ausklammern
554+ p.timeoutperiod:=timeout/SecsPerDay;
555+ *)
556+
557+ //Jede sekunde überprüfen.
558+ p.runcommandsleeptime := 1000;
559+
560+ if ProcessOptions<>[] then
561+ P.Options:=ProcessOptions - [poRunSuspended,poWaitOnExit];
562+ p.options:=p.options+[poRunIdle]; // needed to run the RUNIDLE event. See User Changes 3.2.0
563+
564+ P.ShowWindow:=SwOptions;
565+ p.Executable:=exename;
566+ if high(commands)>=0 then
567+ for i:=low(commands) to high(commands) do
568+ p.Parameters.add(commands[i]);
569+ p.timedout:=false;
570+ p.started:=now;
571+ try
572+ // RunCommandLoop ruft alle runcommandsleeptime Millisekcunden LocalnIdleSleep auf.
573+ RunCommandwithTimeout:=p.RunCommandLoop(outputstring,errorstring,exitstate)=0;
574+ if p.timedout then
575+ RunCommandwithTimeout:=false;
576+ finally
577+ p.free;
578+ end;
579+ if exitstate<>0 then RunCommandwithTimeout:=false;
580+end;
581+
582+// Beispiel:
583+
584+var
585+ output,errors : string;
586+ exitcode :integer;
587+begin
588+ //Windows und Unix haben andere Programme
589+ {$if defined(WINDOWS)}
590+ if TProcessTimeout.RunCommandwithTimeout('cmd',['/c','dir','C:*.exe','/s'],'c:\',output,errors,exitcode,[],swoNone,1) then // dir c:*.exe /s
591+ {$else$}
592+ if TProcessTimeout.RunCommandwithTimeout('find',['/','-name','"*.sh"'],'/',output,errors,exitcode,[],swoNone,1) then // find / -name "*.sh"
593+ {$endif}
594+ begin
595+ writeln('Programm fertig');
596+ writeln('--------------------Ausgabe-------------------');
597+ writeln(output);
598+ writeln('--------------------Fehler--------------------');
599+ writeln(errors);
600+ if not(exitcode=0) then
601+ begin
602+ writeln('-Programm mit Fehler beendet (exitcode: '+exitcode.tostring+')-');
603+ end;
604+ readln();
605+ end
606+ else
607+ begin
608+ writeln('Programm abgebrochen');
609+ writeln('--------------------Ausgabe-------------------');
610+ writeln(output);
611+ writeln('--------------------Fehler--------------------');
612+ writeln(errors);
613+ writeln('-------------------EXITCODE------------------');
614+ writeln(exitcode);
615+ readln();
616+ end;
617+end.
618+```
619+
620+## Siehe auch
621+
622+- [[TXMLPropStorage]]
623+
624+---
625+*From the Free Pascal wiki, page "Executing External Programs/de", as it stood on 2023-04-03 after a change by Yourlahr, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history