Sign in Join
Wiki copytemporary

Executing External Programs/de: what changed

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

OldNewLine
11Zurück zu den [[Additional information/de|Zusätzlichen Informationen]].
22
33## Einleitung
44
55Es gibt verschiedene Wege, ein externes Programm auszuführen, aber ich will mich hier auf einen konzentrieren: [[doc:fcl/process/tprocess.html|TProcess]].
66
77Wenn 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).
88
99> **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)
1010
1111> **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!
1212
1313## Überblick : Vergleich
1414
1515| Unit | Methode | Plattformen | Befehlszeile | Features und Anmerkungen |
1616|----|----|----|----|----|
1717| SysUtils | [ExecuteProcess](https://www.freepascal.org/docs-html/rtl/sysutils/executeprocess.html) | Cross-Platform | Ja | Sehr limitiert, synchron |
1818| 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. |
1919| | [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 |
2020| | [CreateProcess](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx) | Nur MS Windows | Ja | Voll |
2121| Unix | [fpsystem](https://www.freepascal.org/docs-html/rtl/unix/fpsystem.html) | Nur Unix | Ja | Ruft die Systemshell `/bin/sh` auf. |
2222| 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. |
2323| Process | [TProcess](https://www.freepascal.org/docs-html/fcl/process/tprocess.html) | Cross-Platform | Nein | Voll |
2424| 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 |
2525| LCLIntf | [OpenDocument](https://lazarus-ccr.sourceforge.io/docs/lcl/lclintf/opendocument.html) | Cross-Platform | Ja | Nur Dokument öffnen |
2626| LCLIntf | [OpenURL](https://lazarus-ccr.sourceforge.io/docs/lcl/lclintf/openurl.html) | Cross-Platform | Ja | URL öffnen |
2727
2828## (Process.)RunCommand
2929
3030- [RunCommand](http://www.freepascal.org/docs-html/fcl/process/runcommand.html) siehe dort
3131- [RunCommandInDir](http://www.freepascal.org/docs-html/fcl/process/runcommandindir.html) siehe dort
3232
3333Mit FPC 2.6.2 wurden für TProcess einige Hilfsfunktionen zur Unit Process hinzugefügt basierend auf Wrapper im [[Projects using Lazarus|fpcup]]-Projekt.
3434
3535Diese 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.
3636
3737Ein einfaches Beispiel:
3838
3939``` pascal
4040program project1;
4141
4242{$mode objfpc}{$H+}
4343
4444uses
4545 Process;
4646
4747var
4848 s : ansistring;
4949
5050begin
5151
5252if RunCommand('/bin/bash',['-c','echo $PATH'],s) then
5353 writeln(s);
5454
5555end.
5656```
5757
5858Aber bitte beachten Sie, dass nicht alle 'built-in' Aufrufe (z. B. in Linux Alias) funktionieren,\
59591.) weil Alias-Werte nur in interaktiven Shells standardmäßig expandiert werden und\
60602.) weil auch .bashrc nur in interaktiven Shells gelesen wird, es sei denn die BASH_ENV Environment Variable ist gesetzt.\
6161So ergibt z. B. folgendes Programm keinen Output.
6262
6363``` pascal
6464program project2;
6565
6666{$mode objfpc}{$H+}
6767
6868uses
6969 Process;
7070
7171var
7272 s : ansistring;
7373
7474begin
7575
7676if RunCommand('/bin/bash',['-c','alias'],s) then
7777 writeln(s);
7878
7979end.
8080```
8181
8282Eine überladene Variante von RunCommand liefert zusätzlich den Exitcode des Programs. RunCommandInDir setzt noch zusätzlich das Standardverzeichnis (setzt p.CurrentDirectory):
8383
8484``` pascal
8585function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string; var exitstatus:integer): integer;
8686function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string): boolean;
8787function RunCommand(const exename:string;const commands:array of string;var outputstring:string): boolean;
8888```
8989
9090## SysUtils.ExecuteProcess
9191
9292Wenn Sie keine Pipes oder sonstige Kontrollmechanismen benötigen, kann das Ausführen externer Programme über folgenden Befehl erreicht werden:
9393
9494``` pascal
9595SysUtils.ExecuteProcess( UTF8ToSys( '/full/path/to/binary'), '''' '', []);
9696```
9797
9898Leider 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:
9999
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)
100+```text
101+http://msdn.microsoft.com/en-us/library/ms682512%28v=vs.85%29.aspx
102+```
101103
102104## TProcess
103105
104106Man kann TProcess benutzen, um externe Programme zu starten. Vorteile von TProcess sind
105107
106108- Plattform-Unabhängigkeit
107109- die Fähigkeit, von *stdout* zu lesen und auf *stdin* zu schreiben.
108110
109111Anmerkung: 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.
110112
111113Wichtig: 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.
112114
113115### Ein einfaches Beispiel
114116
115117``` pascal
116118// Dies ist ein Demoprogramm, das zeigt, wie man ein
117119// externes Programm startet.
118120program launchprogram;
119121
120122// Hier binden wir Dateien ein, die hilfreiche Funktionen
121123// und Prozeduren haben, die wir benötigen.
122124uses
123125 Classes, SysUtils, Process;
124126
125127// Dies definiert die Variable "AProcess" als eine Variable
126128// vom Typ "TProcess"
127129var
128130 AProcess: TProcess;
129131
130132// Hier beginnt der Befehlstext:
131133begin
132134 // Nun erstellen wir das Objekt TProcess und
133135 // weisen es der Variable AProcess zu.
134136 AProcess := TProcess.Create(nil);
135137
136138 // Lassen sie uns den FreePascal Compiler verwenden.
137139 // Dazu müssen wir den Programmnamen an AProcess
138140 // übergeben:
139141 AProcess.Executable:= 'ppc386';
140142
141143 // An das Programm soll der Parameter -h übergeben werden:
142144 AProcess.Parameters.Add('-h');
143145
144146 // Während das externe Programm läuft, soll unser
145147 // Programm natürlich nicht weiterlaufen.
146148 // Dies regeln wir mit folgender Bedingung:
147149 AProcess.Options := AProcess.Options + [poWaitOnExit];
148150
149151 // Nun muss AProcess noch ausgeführt werden...
150152 AProcess.Execute;
151153
152154 // Wegen der oben gesetzter Bedingung wird dieser
153155 // Codeabschnitt erst erreicht, wenn ppc386 beendet
154156 // ist:
155157 AProcess.Free;
156158end.
157159```
158160
159161Das war's. Sie haben gerade gelernt, wie Sie in Ihrem Programm ein externes Programm aufrufen.
160162
161163### Ein verbessertes Beispiel
162164
163165Das ist nett, aber wie lese ich den Output von einem Programm, das ich laufen habe?
164166
165167Lassen Sie uns unser Beispiel ein wenig erweitern und einfach das tun:
166168
167169``` pascal
168170// Dies ist ein Demoprogramm, das zeigt, wie man ein externes
169171// Programm startet und dessen Output einliest.
170172program launchprogram;
171173
172174// Hier binden wir Dateien ein, die hilfreiche Funktionen
173175// und Prozeduren haben, die wir benötigen.
174176uses
175177 Classes, SysUtils, Process;
176178
177179// Dies definiert die Variable "AProcess" als eine
178180// Variable vom Typ "TProcess"
179181// Jetzt fügen wir auch eine TStringList hinzu, um die vom
180182// Programm ausgegebenen Daten zu speichern
181183var
182184 AProcess: TProcess;
183185 AStringList: TStringList;
184186
185187// Ab hier startet Ihr Programm
186188begin
187189 // Jetzt erzeugen wir das TProcess Objekt und
188190 // ordnen es der Variablen AProcess zu.
189191 AProcess := TProcess.Create(nil);
190192
191193 // Erzeugen des TStringList Objekts.
192194 AStringList := TStringList.Create;
193195
194196 // Gibt an, welcher Befehl vom Prozess ausgeführt werden soll
195197 // Lassen sie uns den FreePascal Compiler verwenden
196198 AProcess.CommandLine := 'ppc386 -h';
197199
198200 // Wir definieren eine Option, wie das Programm
199201 // ausgeführt werden soll. Dies stellt sicher, dass
200202 // unser Programm nicht vor Beendigung des aufgerufenen
201203 // Programmes fortgesetzt wird. Außerdem geben wir an,
202204 // dass wir die Ausgabe lesen wollen
203205 AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
204206
205207 // Startet den Prozess, nachdem die Parameter entsprechend
206208 // gesetzt sind
207209 AProcess.Execute;
208210
209211 // Folgendes wird erst nach Beendigung von ppc386 ausgeführt
210212
211213 // Die Ausgabe wird nun in die Stringliste gelesen
212214 AStringList.LoadFromStream(AProcess.Output);
213215
214216 // Speichert den output in eine Datei.
215217 AStringList.SaveToFile('output.txt');
216218
217219 // Nun da die Datei gespeichert ist können wir
218220 // TStringList und TProcess freigeben.
219221 AStringList.Free;
220222 AProcess.Free;
221223end.
222224```
223225
224226### Einlesen eines großen Outputs
225227
226228Im 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.
227229
228230Das 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.
229231
230232``` pascal
231233program procoutlarge;
232234{
233235 Copyright (c) 2004 by Marc Weustink
234236
235237 This example is creeated in the hope that it will be useful,
236238 but WITHOUT ANY WARRANTY; without even the implied warranty of
237239 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
238240}
239241
240242uses
241243 Classes, Process, SysUtils;
242244
243245const
244246 READ_BYTES = 2048;
245247
246248var
247249 S: TStringList;
248250 M: TMemoryStream;
249251 P: TProcess;
250252 n: LongInt;
251253 BytesRead: LongInt;
252254
253255begin
254256 // Wir können poWaitOnExit hier nicht nutzen, weil wir die
255257 // Größe des Outputs nicht kennen. In Linux ist die Größe der
256258 // Pipe 2kB. Wenn die Ausgabe größer ist, müssen die Daten
257259 // zwischenzeitlich ausgelesen werden. Dies ist nicht möglich,
258260 // wenn auf das Ende gewartet wird - ein Deadlock tritt auf.
259261 //
260262 // Ein temporärer Memorystream wird verwendet, um den output zu puffern.
261263
262264 M := TMemoryStream.Create;
263265 BytesRead := 0;
264266
265267 P := TProcess.Create(nil);
266268 P.CommandLine := 'ppc386 -va bogus.pp';
267269 P.Options := [poUsePipes];
268270 WriteLn('-- executing --');
269271 P.Execute;
270272 while P.Running do
271273 begin
272274 // stellt sicher, dass wir Platz haben
273275 M.SetSize(BytesRead + READ_BYTES);
274276
275277 // versuche, es zu lesen
276278 n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
277279 if n > 0
278280 then begin
279281 Inc(BytesRead, n);
280282 Write('.')
281283 end
282284 else begin
283285 // keine Daten, warte 100 ms
284286 Sleep(100);
285287 end;
286288 end;
287289 // lese den letzten Teil
288290 repeat
289291 // stellt sicher, dass wir Platz haben
290292 M.SetSize(BytesRead + READ_BYTES);
291293 // versuche es zu lesen
292294 n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
293295 if n > 0
294296 then begin
295297 Inc(BytesRead, n);
296298 Write('.');
297299 end;
298300 until n <= 0;
299301 if BytesRead > 0 then WriteLn;
300302 M.SetSize(BytesRead);
301303 WriteLn('-- executed --');
302304
303305 S := TStringList.Create;
304306 S.LoadFromStream(M);
305307 WriteLn('-- linecount = ', S.Count, ' --');
306308 for n := 0 to S.Count - 1 do
307309 begin
308310 WriteLn('| ', S[n]);
309311 end;
310312 WriteLn('-- end --');
311313 S.Free;
312314 P.Free;
313315 M.Free;
314316end.
315317```
316318
317319### Verwendung von input und output eines TProcess
318320
319321Siehe das Beispiel 'processdemo' im [Lazarus-CCR SVN](https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/examples/process).
320322
321323### Hinweise für die Verwendung von TProcess
322324
323325Mit den Direktiven `{$IFDEF}` und `{$ENDIF}` können Sie das Programm je nach Betriebssystem auswählen.
324326
325327``` pascal
326328AProcess:TProcess.Create(nil)
327329{$IFDEF WIN32}
328330AProcess.Executable := 'calc.exe'; // Windows Rechner
329331{$ENDIF}
330332{$IFDEF LINUX}
331333AProcess.Executable := 'kcalc'; // KDE Rechner
332334{$ENDIF}
333335AProcess.Execute; // alternativ können sie AProcess.Active := True verwenden
334336```
335337
336338### Beispiel von "talking" mit dem aspell Prozess
337339
338340Innerhalb 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:
339341
340342- [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.
341343
342344- [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.
343345
344346Beide 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.
345347
346348### Ersetzen von Shell-Operatoren wie "\| \< \>"
347349
348350Gelegentlich möchten Sie einen komplizierteren Befehl starten, der seine Daten an einen anderen Befehl oder eine Datei weiterleitet. Also etwa folgendes
349351
350352``` pascal
351353ShellExecute('ersterBefehl.exe | zweiterBefehl.exe');
352354```
353355
354356oder
355357
356358``` pascal
357359ShellExecute('dir > output.txt');
358360```
359361
360362Das mit TProcess auszuführen klappt nicht, also:
361363
362364``` pascal
363365// das klappt nicht
364366Process.CommandLine := 'ersterBefehl.exe | zweiterBefehl.exe';
365367Process.Execute;
366368```
367369
368370#### Warum die Verwendung von Spezialoperatoren zum Umleiten der Ausgabe nicht funktioniert
369371
370372TProcess 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]].
371373
372374### Wie leitet man die Ausgabe mit TProcess um
373375
374376Sie können die Ausgaben eines Befehls an einen anderen Befehl umleiten, indem Sie für **jeden** Befehl eine Instanz des TProcess verwenden.
375377
376378Hier 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]]
377379
378380Sie 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.
379381
380382``` pascal
381383program Project1;
382384
383385uses
384386 Classes, sysutils, process;
385387
386388var
387389 FirstProcess,
388390 SecondProcess: TProcess;
389391 Buffer: array[0..127] of char;
390392 ReadCount: Integer;
391393 ReadSize: Integer;
392394begin
393395 FirstProcess := TProcess.Create(nil);
394396 SecondProcess := TProcess.Create(nil);
395397
396398 FirstProcess.Options := [poUsePipes];
397399 SecondProcess.Options := [poUsePipes,poStderrToOutPut];
398400
399401 FirstProcess.CommandLine := 'pwd';
400402 SecondProcess.CommandLine := 'grep '+ DirectorySeparator+ ' -';
401403 // dies liefert das gleiche wie "pwd | grep / -"
402404
403405 FirstProcess.Execute;
404406 SecondProcess.Execute;
405407
406408 while FirstProcess.Running or (FirstProcess.Output.NumBytesAvailable > 0) do
407409 begin
408410 if FirstProcess.Output.NumBytesAvailable > 0 then
409411 begin
410412 // stellen Sie sicher, dass Sie nicht mehr Daten lesen als im Puffer
411413 // bereitstehen
412414 ReadSize := FirstProcess.Output.NumBytesAvailable;
413415 if ReadSize > SizeOf(Buffer) then
414416 ReadSize := SizeOf(Buffer);
415417 // jetzt lesen wir die Ausgabe in den Puffer
416418 ReadCount := FirstProcess.Output.Read(Buffer[0], ReadSize);
417419 // und schreiben den Puffer an den zweiten Prozess
418420 SecondProcess.Input.Write(Buffer[0], ReadCount);
419421
420422 // falls der SecondProcess große Datenmengen an seine Ausgabe sendet, dann
421423 // sollten wir diese Daten auslesen, um einen Deadlock zu vermeiden.
422424 // siehe das vorhergehende Beispiel "Einlesen eines großen Outputs"
423425 end;
424426 end;
425427 // Schließt die Eingabe an den SecondProcess,
426428 // sodass der die Verarbeitung seiner Daten beendet
427429 SecondProcess.CloseInput;
428430
429431 // und warte auf seine Beendigung.
430432 // Sei achtsam, welcher Befehl ausgeführt werden soll, weil er nicht beendet würde,
431433 // wenn seine Eingabe geschlossen ist. Die folgende Zeile führt dann zu einer unendlichen Schleife
432434 while SecondProcess.Running do
433435 Sleep(1);
434436 // Das wars! Der Rest des Programms ist nur dazu da, dass das Beispiel
435437 // irgendwas 'nützliches' macht.
436438
437439 // Wir benutzen wieder den Puffer, diesmal für die Ausgabe vom SecondProcess
438440 // an die Standardausgabe stdout dieses Beispielprogrammes.
439441 WriteLn('Grep output Start:');
440442 ReadSize := SecondProcess.Output.NumBytesAvailable;
441443 if ReadSize > SizeOf(Buffer) then
442444 ReadSize := SizeOf(Buffer);
443445 if ReadSize > 0 then
444446 begin
445447 ReadCount := SecondProcess.Output.Read(Buffer, ReadSize);
446448 WriteLn(Copy(Buffer,0, ReadCount));
447449 end
448450 else
449451 WriteLn('grep did not find what we searched for. ', SecondProcess.ExitStatus);
450452 WriteLn('Grep output Finish:');
451453
452454 // free our process objects
453455 FirstProcess.Free;
454456 SecondProcess.Free;
455457end.
456458```
457459
458460Das war es. Nun können Sie Ausgaben von einem Programm an das nächste umleiten.
459461
460462#### Anmerkungen
461463
462464Dieses Beipiel könnte übertrieben erscheinen, weil es ermöglicht, mit TProcess auch "komplizierte" Befehle unter Verwendung einer Shell zu starten, wie:
463465
464466``` pascal
465467Process.Commandline := 'sh -c "pwd | grep / -"';
466468```
467469
468470Aber 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.\
469471Ebenso 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.
470472
471473### Parameter, die Leerzeichen enthalten (Replacing Shell Quotes)
472474
473475In der Linux-Shell ist es möglich, Argumente in Anführungszeichen zu schreiben, ähnlich dem Folgenden:
474476
475-`gdb --batch --eval-command="info symbol 0x0000DDDD" myprogram`
477+```text
478+gdb --batch --eval-command="info symbol 0x0000DDDD" myprogram
479+```
476480
477481Und GDB erhält dadurch 3 Argumente (zusätzlich zum ersten Argument, das den vollständigen Pfad zum ausführbaren Programm enthält):
478482
4794831. --batch
4804842. --eval-command=info symbol 0x0000DDDD
4814853. the full path to myprogram
482486
483487TProcess 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:
484488
485489``` pascal
486490 TProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /home/me/myprogram';
487491```
488492
489493Und beachten Sie auch, dass Sie nur vollständige Pfade übergeben.
490494
491495Siehe dazu auch die Diskussion darüber unter: <http://bugs.freepascal.org/view.php?id=14446>
492496
493497### RunCommand Erweiterungen
494498
495499Im 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:
496500
497501``` pascal
498502program TestTProcessTimeout;
499503
500504uses classes, sysutils, process, dateutils;
501505
502506type
503507 { TProcessTimeout }
504508 TProcessTimeout = class(TProcess)
505509 protected
506510 timeoutperiod: TTime;
507511 timedout : boolean;
508512 started : TDateTime;
509513 procedure LocalnIdleSleep(Sender,Context : TObject;status:TRunCommandEventCode;const message:string);
510514 public
511515 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;
512516 end;
513517
514518procedure TProcessTimeout.LocalnIdleSleep(Sender,Context : TObject;status:TRunCommandEventCode;const message:string);
515519begin
516520 if status=RunCommandIdle then
517521 begin
518522 writeln(Executable+'('+ Parameters.CommaText+')' +' time: '+TimeToStr(now-started));
519523 if (now-started)>timeoutperiod then
520524 begin
521525 writeln('process timed out ');
522526 timedout:=true;
523527 Terminate(255);
524528 exit;
525529 end;
526530 sleep(RunCommandSleepTime);
527531 end;
528532end;
529533
530534class function TProcessTimeout.RunCommandwithTimeout(
531535 const exename : TProcessString;
532536 const commands : array of TProcessString;
533537 const dir : string;
534538 out outputstring : string;
535539 out errorstring : string;
536540 out exitstate : integer;
537541 ProcessOptions : TProcessOptions = [];
538542 SWOptions : TShowWindowOptions=swoNone;
539543 timeout : integer=15
540544 ):boolean;
541545Var
542546 p : TProcessTimeout;
543547 i : integer;
544548begin
545549 p:=TProcessTimeout.create(nil);
546550 p.OnRunCommandEvent:=@p.LocalnIdleSleep;
547551 p.CurrentDirectory:=dir;
548552
549553 //timeout in Minuten
550554 p.timeoutperiod:=timeout/MinsPerDay;
551555
552556 (*
553557 //Wenn der Timeout in sekunden angegeben werden soll dies hier ausklammern
554558 p.timeoutperiod:=timeout/SecsPerDay;
555559 *)
556560
557561 //Jede sekunde überprüfen.
558562 p.runcommandsleeptime := 1000;
559563
560564 if ProcessOptions<>[] then
561565 P.Options:=ProcessOptions - [poRunSuspended,poWaitOnExit];
562566 p.options:=p.options+[poRunIdle]; // needed to run the RUNIDLE event. See User Changes 3.2.0
563567
564568 P.ShowWindow:=SwOptions;
565569 p.Executable:=exename;
566570 if high(commands)>=0 then
567571 for i:=low(commands) to high(commands) do
568572 p.Parameters.add(commands[i]);
569573 p.timedout:=false;
570574 p.started:=now;
571575 try
572576 // RunCommandLoop ruft alle runcommandsleeptime Millisekcunden LocalnIdleSleep auf.
573577 RunCommandwithTimeout:=p.RunCommandLoop(outputstring,errorstring,exitstate)=0;
574578 if p.timedout then
575579 RunCommandwithTimeout:=false;
576580 finally
577581 p.free;
578582 end;
579583 if exitstate<>0 then RunCommandwithTimeout:=false;
580584end;
581585
582586// Beispiel:
583587
584588var
585589 output,errors : string;
586590 exitcode :integer;
587591begin
588592 //Windows und Unix haben andere Programme
589593 {$if defined(WINDOWS)}
590594 if TProcessTimeout.RunCommandwithTimeout('cmd',['/c','dir','C:*.exe','/s'],'c:\',output,errors,exitcode,[],swoNone,1) then // dir c:*.exe /s
591595 {$else$}
592596 if TProcessTimeout.RunCommandwithTimeout('find',['/','-name','"*.sh"'],'/',output,errors,exitcode,[],swoNone,1) then // find / -name "*.sh"
593597 {$endif}
594598 begin
595599 writeln('Programm fertig');
596600 writeln('--------------------Ausgabe-------------------');
597601 writeln(output);
598602 writeln('--------------------Fehler--------------------');
599603 writeln(errors);
600604 if not(exitcode=0) then
601605 begin
602606 writeln('-Programm mit Fehler beendet (exitcode: '+exitcode.tostring+')-');
603607 end;
604608 readln();
605609 end
606610 else
607611 begin
608612 writeln('Programm abgebrochen');
609613 writeln('--------------------Ausgabe-------------------');
610614 writeln(output);
611615 writeln('--------------------Fehler--------------------');
612616 writeln(errors);
613617 writeln('-------------------EXITCODE------------------');
614618 writeln(exitcode);
615619 readln();
616620 end;
617621end.
618622```
619623
620624## Siehe auch
621625
622626- [[TXMLPropStorage]]
623627
624628---
625629*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