Sign in Join
Wiki copytemporary

Executing External Programs: what changed

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

OldNewLine
11## Overview: Comparison
22
33Here are different ways available in RTL, FCL and LCL libraries on how to execute an external command/process/program.
44
55| Method | Library | Platforms | Single Line | Features |
66|----|----|----|----|----|
77| [[Executing External Programs|ExecuteProcess]] | RTL | Cross-Platform | Yes | Very limited, synchronous. |
88| [[Executing External Programs|ShellExecute]] | WinAPI | MS Windows only | Yes | Many. Can start programs with elevation/admin permissions. |
99| [[Executing External Programs|fpsystem, fpexecve]] | Unix | Unix only | | |
1010| [[Executing External Programs|TProcess]] | FCL | Cross-Platform | No | Full. |
1111| [[Executing External Programs|RunCommand]] | FCL | Cross-Platform **Requires FPC 2.6.2+** | Yes | Covers common TProcess usage. |
1212| [[Executing External Programs|OpenDocument]] | LCL | Cross-Platform | Yes | Only open document. The document would open with the application associated with that type of the document. |
1313| [[Executing External Programs|WinExec]] | WinAPI | MS Windows only | Yes Legacy option found in old Delphi code. Replace with one of the above. | |
1414
1515## (Process.)RunCommand
1616
1717- [RunCommand](http://www.freepascal.org/docs-html/fcl/process/runcommand.html) reference
1818- [RunCommandInDir](http://www.freepascal.org/docs-html/fcl/process/runcommandindir.html) reference
1919
2020In FPC 2.6.2, some helper functions for TProcess were added to unit process based on wrappers used in the [[Projects using Lazarus|fpcup]] project. These functions are meant for basic and intermediate use and can capture output to a single string and fully support the *large output* case.
2121
2222A simple example is:
2323
2424``` pascal
2525program project1;
2626
2727{$mode objfpc}{$H+}
2828
2929uses
3030 Process;
3131
3232var
3333 s : ansistring;
3434
3535begin
3636
3737if RunCommand('/bin/bash',['-c','echo $PATH'],s) then
3838 writeln(s);
3939
4040end.
4141```
4242
4343But note that not all shell "built-in" commands (eg alias) work because aliases by default are not expanded in non-interactive shells and .bashrc is not read by non-interactive shells unless you set the BASH_ENV environment variable. So, this does not produce any output:
4444
4545``` pascal
4646program project2;
4747
4848{$mode objfpc}{$H+}
4949
5050uses
5151 Process;
5252
5353var
5454 s : ansistring;
5555
5656begin
5757
5858if RunCommand('/bin/bash',['-c','alias'],s) then
5959 writeln(s);
6060
6161end.
6262```
6363
6464An overloaded variant of RunCommand returns the exitcode of the program. The RunCommandInDir runs the command in a different directory (sets p.CurrentDirectory):
6565
6666``` pascal
6767function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string; var exitstatus:integer): integer;
6868function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string): boolean;
6969function RunCommand(const exename:string;const commands:array of string;var outputstring:string): boolean;
7070```
7171
7272In **FPC 3.2.0+** the Runcommand got additional variants that allow to override TProcessOptions and WindowOptions.
7373
7474### RunCommand extensions
7575
7676In **FPC 3.2.0+** the RunCommand implementation was generalized and integrated back into TProcess to allow for quicker construction of own variants. As an example a RunCommand variant with a timeout:
7777
7878``` pascal
7979
8080program TestTProcessTimeout;
8181
8282uses classes, sysutils, process, dateutils;
8383
8484type
8585 { TProcessTimeout }
8686 TProcessTimeout = class(TProcess)
8787 protected
8888 timeoutperiod: TTime;
8989 timedout : boolean;
9090 started : TDateTime;
9191 procedure LocalnIdleSleep(Sender,Context : TObject;status:TRunCommandEventCode;const message:string);
9292 public
9393 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;
9494 end;
9595
9696procedure TProcessTimeout.LocalnIdleSleep(Sender,Context : TObject;status:TRunCommandEventCode;const message:string);
9797begin
9898 if status=RunCommandIdle then
9999 begin
100100 writeln(Executable+'('+ Parameters.CommaText+')' +' time: '+TimeToStr(now-started));
101101 if (now-started)>timeoutperiod then
102102 begin
103103 writeln('process timed out ');
104104 timedout:=true;
105105 Terminate(255);
106106 exit;
107107 end;
108108 sleep(RunCommandSleepTime);
109109 end;
110110end;
111111
112112class function TProcessTimeout.RunCommandwithTimeout(
113113 const exename : TProcessString;
114114 const commands : array of TProcessString;
115115 const dir : string;
116116 out outputstring : string;
117117 out errorstring : string;
118118 out exitstate : integer;
119119 ProcessOptions : TProcessOptions = [];
120120 SWOptions : TShowWindowOptions=swoNone;
121121 timeout : integer=15
122122 ):boolean;
123123Var
124124 p : TProcessTimeout;
125125 i : integer;
126126begin
127127 p:=TProcessTimeout.create(nil);
128128 p.OnRunCommandEvent:=@p.LocalnIdleSleep;
129129 p.CurrentDirectory:=dir;
130130
131131 //timeout in Minutes, Check every Seconds
132132 p.timeoutperiod:=timeout/MinsPerDay;
133133
134134 (*
135135 //if you want to do the Timeout in Seconds
136136 p.timeoutperiod:=timeout/SecsPerDay;
137137 *)
138138
139139 // check every Second if finished
140140 p.runcommandsleeptime := 1000;
141141
142142 if ProcessOptions<>[] then
143143 P.Options:=ProcessOptions - [poRunSuspended,poWaitOnExit];
144144 p.options:=p.options+[poRunIdle]; // needed to run the RUNIDLE event. See User Changes 3.2.0
145145
146146 P.ShowWindow:=SwOptions;
147147 p.Executable:=exename;
148148 if high(commands)>=0 then
149149 for i:=low(commands) to high(commands) do
150150 p.Parameters.add(commands[i]);
151151 p.timedout:=false;
152152 p.started:=now;
153153 try
154154 // the core loop of runcommand() variants, originally based on the "large output" scenario in the wiki, but continously expanded over 5 years.
155155 RunCommandwithTimeout:=p.RunCommandLoop(outputstring,errorstring,exitstate)=0;
156156 if p.timedout then
157157 RunCommandwithTimeout:=false;
158158 finally
159159 p.free;
160160 end;
161161 if exitstate<>0 then RunCommandwithTimeout:=false;
162162end;
163163
164164// example use:
165165
166166var
167167 output,errors : string;
168168 exitcode :integer;
169169begin
170170 //Windows and Unix have other programs that can be used
171171 {$if defined(WINDOWS)}
172172 if TProcessTimeout.RunCommandwithTimeout('cmd',['/c','dir','C:*.exe','/s'],'c:\',output,errors,exitcode,[],swoNone,1) then // dir c:*.exe /s
173173 {$else$}
174174 if TProcessTimeout.RunCommandwithTimeout('find',['/','-name','"*.sh"'],'/',output,errors,exitcode,[],swoNone,1) then // find / -name "*.sh"
175175 {$endif}
176176 begin
177177 writeln('program finished');
178178 writeln('--------------------output-------------------');
179179 writeln(output);
180180 writeln('--------------------errors-------------------');
181181 writeln(errors);
182182 if not(exitcode=0) then
183183 begin
184184 writeln('-program finished with errors (exitcode: '+exitcode.tostring+')-');
185185 end;
186186 readln();
187187 end
188188 else
189189 begin
190190 writeln('program not finished');
191191 writeln('--------------------output-------------------');
192192 writeln(output);
193193 writeln('--------------------errors-------------------');
194194 writeln(errors);
195195 writeln('-------------------EXITCODE------------------');
196196 writeln(exitcode);
197197 readln();
198198 end;
199199end.
200200```
201201
202202## SysUtils.ExecuteProcess
203203
204204(Cross-platform)\
205205Despite a number of limitations, the simplest way to launch a program (modal, no pipes or any form of control) is to simply use :
206206
207207``` pascal
208208SysUtils.ExecuteProcess(UTF8ToSys('/full/path/to/binary'), '', []);
209209```
210210
211211The calling process runs synchronously: it 'hangs' until the external program has finished - but this may be useful if you require the user to do something before continuing in your application. For a more versatile approach, see the section about the preferred cross-platform **RunCommand** or other **TProcess** functionality, or if you only wish to target Windows you may use **ShellExecute**.
212212
213213- [ExecuteProcess reference](http://www.freepascal.org/docs-html/rtl/sysutils/executeprocess.html)
214214
215215## MS Windows: CreateProcess, ShellExecute and WinExec
216216
217217> **Note:** While FPC/Lazarus has support for **CreateProcess**, **ShellExecute** and/or **WinExec**, this support is only in Win32/64. If your program is cross-platform, consider using **RunCommand** or **TProcess**.
218218
219219> **Note:** WinExec is a 16-bit call that has been deprecated for years in the Windows API. In recent versions of FPC it generates a warning.
220220
221221**ShellExecute** is a standard MS Windows function (ShellApi.h) with good \[<http://msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85>).aspx documentation on MSDN\] (note their remarks about initialising COM if you find the function unreliable).
222222
223223``` pascal
224224uses ..., ShellApi;
225225
226226// Simple one-liner (ignoring error returns) :
227227if ShellExecute(0,nil, PChar('"C:\my dir\prog.exe"'),PChar('"C:\somepath\some_doc.ext"'),nil,1) =0 then;
228228
229229// Execute a Batch File :
230230if ShellExecute(0,nil, PChar('cmd'),PChar('/c mybatch.bat'),nil,1) =0 then;
231231
232232// Open a command window in a given folder :
233233if ShellExecute(0,nil, PChar('cmd'),PChar('/k cd \path'),nil,1) =0 then;
234234
235235// Open a webpage URL in the default browser using 'start' command (via a brief hidden cmd window) :
236236if ShellExecute(0,nil, PChar('cmd'),PChar('/c start www.lazarus.freepascal.org/'),nil,0) =0 then;
237237
238238// or a useful procedure:
239239procedure RunShellExecute(const prog,params:string);
240240begin
241241 // ( Handle, nil/'open'/'edit'/'find'/'explore'/'print', // 'open' isn't always needed
242242 // path+prog, params, working folder,
243243 // 0=hide / 1=SW_SHOWNORMAL / 3=max / 7=min) // for SW_ constants : uses ... Windows ...
244244 if ShellExecute(0,'open',PChar(prog),PChar(params),PChar(extractfilepath(prog)),1) >32 then; //success
245245 // return values 0..32 are errors
246246end;
247247```
248248
249249There is also ShellExecuteExW as a WideChar version, and ShellExecuteExA is AnsiChar.
250250
251251The fMask option can also use SEE_MASK_DOENVSUBST or SEE_MASK_FLAG_NO_UI or SEE_MASK_NOCLOSEPROCESS, etc.
252252
253253If in Delphi you used ShellExecute for **documents** like Word documents or URLs, have a look at the open* (OpenURL etc) functions in lclintf (see the Alternatives section lower down this page).
254254
255255### Using ShellExecuteEx for elevation/administrator permissions
256256
257257If you need to execute external program with administrator/elevated privileges, you can use the **runas** method with the alternative ShellExecuteEx function:
258258
259259``` pascal
260260uses ShellApi, ...;
261261
262262function RunAsAdmin(const Handle: Hwnd; const Path, Params: string): Boolean;
263263var
264264 sei: TShellExecuteInfoA;
265265begin
266266 FillChar(sei, SizeOf(sei), 0);
267267 sei.cbSize := SizeOf(sei);
268268 sei.Wnd := Handle;
269269 sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
270270 sei.lpVerb := 'runas';
271271 sei.lpFile := PAnsiChar(Path);
272272 sei.lpParameters := PAnsiChar(Params);
273273 sei.nShow := SW_SHOWNORMAL;
274274 Result := ShellExecuteExA(@sei);
275275end;
276276
277277procedure TFormMain.RunAddOrRemoveApplication;
278278begin
279279 // Example that uses elevated rundll to open the Control Panel to Programs and features
280280 RunAsAdmin(FormMain.Handle, 'rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl', '');
281281end;
282282```
283283
284284## Unix fpsystem, fpexecve and shell
285285
286286These functions are platform dependent.
287287
288288- [fpsystem reference](http://www.freepascal.org/docs-html/rtl/unix/fpsystem.html)
289289- [fpexecve reference](http://www.freepascal.org/docs-html/rtl/baseunix/fpexecve.html)
290290
291291Linux.Shell/Unix.Shell was an 1.0.x equivalent to fpsystem with less narrowly defined error handling, and after a decade of deprecation it was finally removed. In nearly all cases it can be replaced by fpsystem that features more POSIX like errorhandling.
292292
293293## TProcess
294294
295295You can use TProcess to launch external programs. Some of the benefits of using TProcess are that it is:
296296
297297- Platform Independent
298298- Capable of reading from stdout and writing to stdin.
299299- Possible to wait for a command to finish or let it run while your program moves on.
300300
301301Important notes:
302302
303303- TProcess is not a terminal/shell! You cannot directly execute scripts or redirect output using operators like "\|", "\>", "\<", "&" etc. It is possible to obtain the same results with TProcess using pascal, some examples are below..
304304- Presumably on Linux/Unix: you **must** specify the full path to the executable. For example '/bin/cp' instead of 'cp'. If the program is in the standard PATH then you can use the function [[doc:lcl/fileutil/finddefaultexecutablepath.html|FindDefaultExecutablePath]] from the [[doc:lcl/fileutil/index.html|FileUtil]] unit of the LCL.
305305- On Windows, if the command is in the path, you don't need to specify the full path.
306306- [[doc:fcl/process/tprocess.html|TProcess reference]]
307307
308308### The Simplest Example
309309
310310A lot of typical cases have been prepared in the [[Executing External Programs|Runcommand]] functions. Before you start copy and paste the examples below, check them out first.
311311
312312### A Simple Example
313313
314314This example (**that shouldn't be used in production, see Large Output or, better, [[Executing External Programs|Runcommand]]**) just shows you how to run an external program, nothing more:
315315
316316``` pascal
317317// This is a demo program that shows
318318// how to launch an external program.
319319program launchprogram;
320320
321321// Here we include files that have useful functions
322322// and procedures we will need.
323323uses
324324 Classes, SysUtils, Process;
325325
326326// This defines the var "AProcess" as a variable
327327// of the type "TProcess"
328328var
329329 AProcess: TProcess;
330330
331331// This is where our program starts to run
332332begin
333333 // Now we will create the TProcess object, and
334334 // assign it to the var AProcess.
335335 AProcess := TProcess.Create(nil);
336336
337337 // Tell the new AProcess what the command to execute is.
338338 // Let's use the Free Pascal compiler (i386 version that is)
339339 AProcess.Executable:= 'ppc386';
340340
341341 // Pass -h together with ppc386 so actually 'ppc386 -h' is executed:
342342 AProcess.Parameters.Add('-h');
343343
344344 // We will define an option for when the program
345345 // is run. This option will make sure that our program
346346 // does not continue until the program we will launch
347347 // has stopped running. vvvvvvvvvvvvvv
348348 AProcess.Options := AProcess.Options + [poWaitOnExit];
349349
350350 // Now let AProcess run the program
351351 AProcess.Execute;
352352
353353 // This is not reached until ppc386 stops running.
354354 AProcess.Free;
355355end.
356356```
357357
358358That's it! You have just learned to run an external program from inside your own program.
359359
360360### An improved example (but not correct yet)
361361
362362That's nice, but how do I read the Output of a program that I have run?
363363
364364Well, let's expand our example a little and do just that: **This example is kept simple so you can learn from it. Please don't use this example in production code, but use the code in #Reading large output.**
365365
366366``` pascal
367367// This is a
368368// FLAWED
369369// demo program that shows
370370// how to launch an external program
371371// and read from its output.
372372program launchprogram;
373373
374374// Here we include files that have useful functions
375375// and procedures we will need.
376376uses
377377 Classes, SysUtils, Process;
378378
379379// This is defining the var "AProcess" as a variable
380380// of the type "TProcess"
381381// Also now we are adding a TStringList to store the
382382// data read from the programs output.
383383var
384384 AProcess: TProcess;
385385 AStringList: TStringList;
386386
387387// This is where our program starts to run
388388begin
389389 // Now we will create the TProcess object, and
390390 // assign it to the var AProcess.
391391 AProcess := TProcess.Create(nil);
392392
393393 // Tell the new AProcess what the command to execute is.
394394 AProcess.Executable := '/usr/bin/ppc386';
395395 AProcess.Parameters.Add('-h');
396396
397397 // We will define an option for when the program
398398 // is run. This option will make sure that our program
399399 // does not continue until the program we will launch
400400 // has stopped running. Also now we will tell it that
401401 // we want to read the output of the file.
402402 AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
403403
404404 // Now that AProcess knows what the commandline is it can be run.
405405 AProcess.Execute;
406406
407407 // After AProcess has finished, the rest of the program will be executed.
408408
409409 // Now read the output of the program we just ran into a TStringList.
410410 AStringList := TStringList.Create;
411411 AStringList.LoadFromStream(AProcess.Output);
412412
413413 // Save the output to a file and clean up the TStringList.
414414 AStringList.SaveToFile('output.txt');
415415 AStringList.Free;
416416
417417 // Now that the output from the process is processed, it can be freed.
418418 AProcess.Free;
419419end.
420420```
421421
422422### Reading large output
423423
424424In the previous example we waited until the program exited. Then we read what the program has written to its output.
425425
426426Suppose the program writes a lot of data to the output. Then the output pipe becomes full and the called progam waits until the pipe has been read from.
427427
428428But the calling program doesn't read from it until the called program has ended. A deadlock occurs.
429429
430430The following example therefore doesn't use poWaitOnExit, but reads from the output while the program is still running. The output is stored in a memory stream, that can be used later to read the output into a TStringList.
431431
432432If you want to read output from an external process, and can't use RunCommand, this is the code you should as a base for production use. If you are using **FPC 3.2.0+** a parametrisable form of this loop is available as the method **RunCommandLoop** in TProcess. An event **OnRunCommandEvent** can be hooked to further modify the behaviour.
433433
434434``` pascal
435435program LargeOutputDemo;
436436
437437{$mode objfpc}{$H+}
438438
439439uses
440440 Classes, SysUtils, Process; // Process is the unit that holds TProcess
441441
442442const
443443 BUF_SIZE = 2048; // Buffer size for reading the output in chunks
444444
445445var
446446 AProcess : TProcess;
447447 OutputStream : TStream;
448448 BytesRead : longint;
449449 Buffer : array[1..BUF_SIZE] of byte;
450450
451451begin
452452 // Set up the process; as an example a recursive directory search is used
453453 // because that will usually result in a lot of data.
454454 AProcess := TProcess.Create(nil);
455455
456456 // The commands for Windows and *nix are different hence the $IFDEFs
457457 {$IFDEF Windows}
458458 // In Windows the dir command cannot be used directly because it's a build-in
459459 // shell command. Therefore cmd.exe and the extra parameters are needed.
460460 AProcess.Executable := 'c:\windows\system32\cmd.exe';
461461 AProcess.Parameters.Add('/c');
462462 AProcess.Parameters.Add('dir /s c:\windows');
463463 {$ENDIF Windows}
464464
465465 {$IFDEF Unix}
466466 AProcess.Executable := '/bin/ls';
467467
468468 {$IFDEF Darwin}
469469 AProcess.Parameters.Add('-recursive');
470470 AProcess.Parameters.Add('-all');
471471 {$ENDIF Darwin}
472472
473473 {$IFDEF Linux}
474474 AProcess.Parameters.Add('--recursive');
475475 AProcess.Parameters.Add('--all');
476476 {$ENDIF Linux}
477477
478478 {$IFDEF FreeBSD}
479479 AProcess.Parameters.Add('-R');
480480 AProcess.Parameters.Add('-a');
481481 {$ENDIF FreeBSD}
482482
483483 AProcess.Parameters.Add('-l');
484484 {$ENDIF Unix}
485485
486486 // Process option poUsePipes has to be used so the output can be captured.
487487 // Process option poWaitOnExit can not be used because that would block
488488 // this program, preventing it from reading the output data of the process.
489489 AProcess.Options := [poUsePipes];
490490
491491 // Start the process (run the dir/ls command)
492492 AProcess.Execute;
493493
494494 // Create a stream object to store the generated output in. This could
495495 // also be a file stream to directly save the output to disk.
496496 OutputStream := TMemoryStream.Create;
497497
498498 // All generated output from AProcess is read in a loop until no more data is available
499499 repeat
500500 // Get the new data from the process to a maximum of the buffer size that was allocated.
501501 // Note that all read(...) calls will block except for the last one, which returns 0 (zero).
502502 BytesRead := AProcess.Output.Read(Buffer, BUF_SIZE);
503503
504504 // Add the bytes that were read to the stream for later usage
505505 OutputStream.Write(Buffer, BytesRead)
506506
507507 until BytesRead = 0; // Stop if no more data is available
508508
509509 // The process has finished so it can be cleaned up
510510 AProcess.Free;
511511
512512 // Now that all data has been read it can be used; for example to save it to a file on disk
513513 with TFileStream.Create('output.txt', fmCreate) do
514514 begin
515515 OutputStream.Position := 0; // Required to make sure all data is copied from the start
516516 CopyFrom(OutputStream, OutputStream.Size);
517517 Free
518518 end;
519519
520520 // Or the data can be shown on screen
521521 with TStringList.Create do
522522 begin
523523 OutputStream.Position := 0; // Required to make sure all data is copied from the start
524524 LoadFromStream(OutputStream);
525525 writeln(Text);
526526 writeln('--- Number of lines = ', Count, '----');
527527 Free
528528 end;
529529
530530 // Clean up
531531 OutputStream.Free;
532532end.
533533```
534534
535535Note that the above could also be accomplished by using RunCommand:
536536
537537``` pascal
538538var s: string;
539539...
540540RunCommand('c:\windows\system32\cmd.exe', ['/c', 'dir /s c:\windows'], s);
541541```
542542
543543### Using input and output of a TProcess
544544
545545See processdemo example in the [Lazarus-CCR SVN](https://sourceforge.net/p/lazarus-ccr/svn/HEAD/tree/examples/process).
546546
547547### Hints on the use of TProcess
548548
549549When creating a cross-platform program, the OS-specific executable name can be set using directives "{\$IFDEF}" and "{\$ENDIF}".
550550
551551Example:
552552
553553``` pascal
554554{...}
555555AProcess := TProcess.Create(nil)
556556
557557{$IFDEF WIN32}
558558 AProcess.Executable := 'calc.exe';
559559{$ENDIF}
560560
561561{$IFDEF LINUX}
562562 AProcess.Executable := FindDefaultExecutablePath('kcalc');
563563{$ENDIF}
564564
565565AProcess.Execute;
566566{...}
567567```
568568
569569### macOS show application bundle in foreground
570570
571571You can start an **application bundle** via TProcess by starting the executable within the bundle. For example:
572572
573573``` pascal
574574 AProcess.Executable:='/Applications/iCal.app/Contents/MacOS/iCal';
575575```
576576
577577This will start the *Calendar*, but the window will be behind the current application. To get the application in the foreground you can use the **open** utility with the **-n** parameter:
578578
579579``` pascal
580580 AProcess.Executable:='/usr/bin/open';
581581 AProcess.Parameters.Add('-n');
582582 AProcess.Parameters.Add('-a'); // optional: specifies the application to use; only searches the Application directories
583583 AProcess.Parameters.Add('-W'); // optional: open waits until the applications it opens (or were already open) have exited
584584 AProcess.Parameters.Add('Pages.app'); // including .app is optional
585585```
586586
587587If your application needs parameters, you can pass **open** the **--args** parameter, after which all parameters are passed to the application:
588588
589589``` pascal
590590 AProcess.Parameters.Add('--args');
591591 AProcess.Parameters.Add('argument1');
592592 AProcess.Parameters.Add('argument2');
593593```
594594
595595See also: [[macOS Open Sesame|macOS open command]].
596596
597597### Run detached program
598598
599599Normally a program started by your application is a child process and is killed, when your application is killed. When you want to run a standalone program that keeps running, you can use the following:
600600
601601``` pascal
602602var
603603 Process: TProcess;
604604 I: Integer;
605605begin
606606 Process := TProcess.Create(nil);
607607 try
608608 Process.InheritHandles := False;
609609 Process.Options := [];
610610 Process.ShowWindow := swoShow;
611611
612612 // Copy default environment variables including DISPLAY variable for GUI application to work
613613 for I := 1 to GetEnvironmentVariableCount do
614614 Process.Environment.Add(GetEnvironmentString(I));
615615
616616 Process.Executable := '/usr/bin/gedit';
617617 Process.Execute;
618618 finally
619619 Process.Free;
620620 end;
621621end;
622622```
623623
624624### Example of "talking" with aspell process
625625
626626Inside [pasdoc](https://github.com/pasdoc/pasdoc/wiki) source code you can find two units that perform spell-checking by "talking" with running aspell process through pipes:
627627
628628- [PasDoc_ProcessLineTalk.pas unit](https://github.com/pasdoc/pasdoc/blob/master/source/component/PasDoc_ProcessLineTalk.pas) implements TProcessLineTalk class, descendant of TProcess, that can be easily used to talk with any process on a line-by-line basis.
629629
630630- [PasDoc_Aspell.pas units](https://github.com/pasdoc/pasdoc/blob/master/source/component/PasDoc_Aspell.pas) implements TAspellProcess class, that performs spell-checking by using underlying TProcessLineTalk instance to execute aspell and communicate with running aspell process.
631631
632632Both units are rather independent from the rest of pasdoc sources, so they may serve as real-world examples of using TProcess to run and communicate through pipes with other program.
633633
634634### Replacing shell operators like "\| \< \>"
635635
636636Sometimes you want to run a more complicated command that pipes its data to another command or to a file. Something like
637637
638638``` pascal
639639ShellExecute('firstcommand.exe | secondcommand.exe');
640640```
641641
642642or
643643
644644``` pascal
645645ShellExecute('dir > output.txt');
646646```
647647
648648Executing this with TProcess will not work. i.e:
649649
650650``` pascal
651651// this won't work
652652Process.CommandLine := 'firstcommand.exe | secondcommand.exe';
653653Process.Execute;
654654```
655655
656656#### Why using special operators to redirect output doesn't work
657657
658658TProcess 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|next section]].
659659
660660### How to redirect output with TProcess
661661
662662You can redirect the output of a command to another command by using a TProcess instance for **each** command.
663663
664664Here'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]]
665665
666666Not only can you redirect the "normal" output (also known as stdout), but you can also redirect the error output (stderr), if you specify the poStderrToOutPut option, as seen in the options for the second process.
667667
668668``` pascal
669669program Project1;
670670
671671uses
672672 Classes, sysutils, process;
673673
674674var
675675 FirstProcess,
676676 SecondProcess: TProcess;
677677 Buffer: array[0..127] of char;
678678 ReadCount: Integer;
679679 ReadSize: Integer;
680680begin
681681 FirstProcess := TProcess.Create(nil);
682682 SecondProcess := TProcess.Create(nil);
683683
684684 FirstProcess.Options := [poUsePipes];
685685 FirstProcess.Executable := 'pwd';
686686
687687 SecondProcess.Options := [poUsePipes,poStderrToOutPut];
688688 SecondProcess.Executable := 'grep';
689689 SecondProcess.Parameters.Add(DirectorySeparator+ ' -');
690690 // this would be the same as "pwd | grep / -"
691691
692692 FirstProcess.Execute;
693693 SecondProcess.Execute;
694694
695695 while FirstProcess.Running or (FirstProcess.Output.NumBytesAvailable > 0) do
696696 begin
697697 if FirstProcess.Output.NumBytesAvailable > 0 then
698698 begin
699699 // make sure that we don't read more data than we have allocated
700700 // in the buffer
701701 ReadSize := FirstProcess.Output.NumBytesAvailable;
702702 if ReadSize > SizeOf(Buffer) then
703703 ReadSize := SizeOf(Buffer);
704704 // now read the output into the buffer
705705 ReadCount := FirstProcess.Output.Read(Buffer[0], ReadSize);
706706 // and write the buffer to the second process
707707 SecondProcess.Input.Write(Buffer[0], ReadCount);
708708
709709 // if SecondProcess writes much data to it's Output then
710710 // we should read that data here to prevent a deadlock
711711 // see the previous example "Reading Large Output"
712712 end;
713713 end;
714714 // Close the input on the SecondProcess
715715 // so it finishes processing it's data
716716 SecondProcess.CloseInput;
717717
718718 // and wait for it to complete
719719 // be carefull what command you run because it may not exit when
720720 // it's input is closed and the following line may loop forever
721721 while SecondProcess.Running do
722722 Sleep(1);
723723 // that's it! the rest of the program is just so the example
724724 // is a little 'useful'
725725
726726 // we will reuse Buffer to output the SecondProcess's
727727 // output to *this* programs stdout
728728 WriteLn('Grep output Start:');
729729 ReadSize := SecondProcess.Output.NumBytesAvailable;
730730 if ReadSize > SizeOf(Buffer) then
731731 ReadSize := SizeOf(Buffer);
732732 if ReadSize > 0 then
733733 begin
734734 ReadCount := SecondProcess.Output.Read(Buffer, ReadSize);
735735 WriteLn(Copy(Buffer,0, ReadCount));
736736 end
737737 else
738738 WriteLn('grep did not find what we searched for. ', SecondProcess.ExitStatus);
739739 WriteLn('Grep output Finish:');
740740
741741 // free our process objects
742742 FirstProcess.Free;
743743 SecondProcess.Free;
744744end.
745745```
746746
747747That's it. Now you can redirect output from one program to another.
748748
749749#### Notes
750750
751751This example may seem overdone since it's possible to run "complicated" commands using a shell with TProcess like:
752752
753753``` pascal
754754Process.Commandline := 'sh -c "pwd | grep / -"';
755755```
756756
757757But 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.
758758
759759### Redirecting input and output and running under root
760760
761761A common problem on Unixes (FreeBSD, macOS) and Linux is that you want to execute some program under the root account (or, more generally, another user account). An example would be running the *ping* command.
762762
763763If you can use sudo for this, you could adapt the following example adapted from one posted by andyman on the forum ([1](http://lazarus.freepascal.org/index.php/topic,14479.0.html)). This sample runs `ls` on the `/root` directory, but can of course be adapted.
764764
765765A **better way** to do this is to use the policykit package, which should be available on all recent Linuxes. [See the forum thread for details.](http://lazarus.freepascal.org/index.php/topic,14479.0.html)
766766
767767Large parts of this code are similar to the earlier example, but it also shows how to redirect stdout and stderr of the process being called separately to stdout and stderr of our own code.
768768
769769``` pascal
770770program rootls;
771771
772772{ Demonstrates using TProcess, redirecting stdout/stderr to our stdout/stderr,
773773calling sudo on FreeBSD/Linux/macOS, and supplying input on stdin}
774774{$mode objfpc}{$H+}
775775
776776uses
777777 Classes,
778778 Math, {for min}
779779 Process;
780780
781781 procedure RunsLsRoot;
782782 var
783783 Proc: TProcess;
784784 CharBuffer: array [0..511] of char;
785785 ReadCount: integer;
786786 ExitCode: integer;
787787 SudoPassword: string;
788788 begin
789789 WriteLn('Please enter the sudo password:');
790790 Readln(SudoPassword);
791791 ExitCode := -1; //Start out with failure, let's see later if it works
792792 Proc := TProcess.Create(nil); //Create a new process
793793 try
794794 Proc.Options := [poUsePipes, poStderrToOutPut]; //Use pipes to redirect program stdin,stdout,stderr
795795 Proc.CommandLine := 'sudo -S ls /root'; //Run ls /root as root using sudo
796796 // -S causes sudo to read the password from stdin.
797797 Proc.Execute; //start it. sudo will now probably ask for a password
798798
799799 // write the password to stdin of the sudo program:
800800 SudoPassword := SudoPassword + LineEnding;
801801 Proc.Input.Write(SudoPassword[1], Length(SudoPassword));
802802 SudoPassword := '%*'; //short string, hope this will scramble memory a bit; note: using PChars is more fool-proof
803803 SudoPassword := ''; // and make the program a bit safer from snooping?!?
804804
805805 // main loop to read output from stdout and stderr of sudo
806806 while Proc.Running or (Proc.Output.NumBytesAvailable > 0) or
807807 (Proc.Stderr.NumBytesAvailable > 0) do
808808 begin
809809 // read stdout and write to our stdout
810810 while Proc.Output.NumBytesAvailable > 0 do
811811 begin
812812 ReadCount := Min(512, Proc.Output.NumBytesAvailable); //Read up to buffer, not more
813813 Proc.Output.Read(CharBuffer, ReadCount);
814814 Write(StdOut, Copy(CharBuffer, 0, ReadCount));
815815 end;
816816 // read stderr and write to our stderr
817817 while Proc.Stderr.NumBytesAvailable > 0 do
818818 begin
819819 ReadCount := Min(512, Proc.Stderr.NumBytesAvailable); //Read up to buffer, not more
820820 Proc.Stderr.Read(CharBuffer, ReadCount);
821821 Write(StdErr, Copy(CharBuffer, 0, ReadCount));
822822 end;
823823 end;
824824 ExitCode := Proc.ExitStatus;
825825 finally
826826 Proc.Free;
827827 Halt(ExitCode);
828828 end;
829829 end;
830830
831831begin
832832 RunsLsRoot;
833833end.
834834```
835835
836836Other thoughts: It would no doubt be advisable to see if sudo actually prompts for a password. This can be checked consistently by setting the environment variable SUDO_PROMPT to something we watch for while reading the stdout of TProcess avoiding the problem of the prompt being different for different locales. Setting an environment variable causes the default values to be cleared(inherited from our process) so we have to copy the environment from our program if needed.
837837
838838### Using fdisk with sudo on Linux
839839
840840The following example shows how to run fdisk on a Linux machine using the sudo command to get root permissions. **Note: this is an example only, and does not cater for large output.**
841841
842842``` pascal
843843program getpartitioninfo;
844844{Originally contributed by Lazarus forums wjackon153. Please contact him for questions, remarks etc.
845845Modified from Lazarus snippet to FPC program for ease of understanding/conciseness by BigChimp}
846846
847847Uses
848848 Classes, SysUtils, FileUtil, Process;
849849
850850var
851851 hprocess: TProcess;
852852 sPass: String;
853853 OutputLines: TStringList;
854854
855855begin
856856 sPass := 'yoursudopasswordhere'; // You need to change this to your own sudo password
857857 OutputLines:=TStringList.Create; //... a try...finally block would be nice to make sure
858858 // OutputLines is freed... Same for hProcess.
859859
860860 // The following example will open fdisk in the background and give us partition information
861861 // Since fdisk requires elevated priviledges we need to
862862 // pass our password as a parameter to sudo using the -S
863863 // option, so it will will wait till our program sends our password to the sudo application
864864 hProcess := TProcess.Create(nil);
865865 // On Linux/Unix/FreeBSD/macOS, we need specify full path to our executable:
866866 hProcess.Executable := '/bin/sh';
867867 // Now we add all the parameters on the command line:
868868 hprocess.Parameters.Add('-c');
869869 // Here we pipe the password to the sudo command which then executes fdisk -l:
870870 hprocess.Parameters.add('echo ' + sPass + ' | sudo -S fdisk -l');
871871 // Run asynchronously (wait for process to exit) and use pipes so we can read the output pipe
872872 hProcess.Options := hProcess.Options + [poWaitOnExit, poUsePipes];
873873 // Now run:
874874 hProcess.Execute;
875875
876876 // hProcess should have now run the external executable (because we use poWaitOnExit).
877877 // Now you can process the process output (standard output and standard error), eg:
878878 OutputLines.Add('stdout:');
879879 OutputLines.LoadFromStream(hprocess.Output);
880880 OutputLines.Add('stderr:');
881881 OutputLines.LoadFromStream(hProcess.Stderr);
882882 // Show output on screen:
883883 writeln(OutputLines.Text);
884884
885885 // Clean up to avoid memory leaks:
886886 hProcess.Free;
887887 OutputLines.Free;
888888
889889 //Below are some examples as you see we can pass illegal characters just as if done from terminal
890890 //Even though you have read elsewhere that you can not I assure with this method you can :)
891891
892892 //hprocess.Parameters.Add('ping -c 1 www.google.com');
893893 //hprocess.Parameters.Add('ifconfig wlan0 | grep ' + QuotedStr('inet addr:') + ' | cut -d: -f2');
894894
895895 //Using QuotedStr() is not a requirement though it makes for cleaner code;
896896 //you can use double quote and have the same effect.
897897
898898 //hprocess.Parameters.Add('glxinfo | grep direct');
899899
900900 // This method can also be used for installing applications from your repository:
901901
902902 //hprocess.Parameters.add('echo ' + sPass + ' | sudo -S apt-get install -y pkg-name');
903903
904904 end.
905905```
906906
907907### Parameters which contain spaces (Replacing Shell Quotes)
908908
909909In the Linux shell it is possible to write quoted arguments like this:
910910
911-`gdb --batch --eval-command="info symbol 0x0000DDDD" myprogram`
911+```text
912+gdb --batch --eval-command="info symbol 0x0000DDDD" myprogram
913+```
912914
913915And GDB will receive 3 arguments (in addition to the first argument which is the full path to the executable):
914916
9159171. --batch
9169182. --eval-command=info symbol 0x0000DDDD
9179193. the full path to myprogram
918920
919921The best solution to avoid complicated quoting is to to switch to TProcess.Parameters.Add instead of setting the Commandline for non trivial cases e.g.
920922
921-` AProcess.Executable:='/usr/bin/gdb';`\
922-` AProcess.Parameters.Add('--batch');`\
923-` AProcess.Parameters.Add('--eval-command=info symbol 0x0000DDDD'); // note the absence of quoting here`\
924-` AProcess.Parameters.Add('/home/me/myprogram');`
923+``` pascal
924+ AProcess.Executable:='/usr/bin/gdb';
925+ AProcess.Parameters.Add('--batch');
926+ AProcess.Parameters.Add('--eval-command=info symbol 0x0000DDDD'); // note the absence of quoting here
927+ AProcess.Parameters.Add('/home/me/myprogram');
928+```
925929
926930And also remember to only pass full paths.
927931
928932TProcess.Commandline however does support some basic quoting for parameters with quoting. Quote the *whole* parameter containing spaces with double quotes. Like this:
929933
930-`AProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /home/me/myprogram';`
934+``` pascal
935+AProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /home/me/myprogram';
936+```
931937
932938The .CommandLine property is [deprecated](http://bugs.freepascal.org/view.php?id=14446) and bugreports to support more complicated quoting cases won't accepted.
933939
934940## LCLIntf Alternatives
935941
936942Sometimes, you don't need to explicitly call an external program to get the functionality you need. Instead of opening an application and specifying the document to go with it, just ask the OS to open the document and let it use the default application associated with that file type. Below are some examples.
937943
938944### Open document in default application
939945
940946In some situations you need to open some document/file using default associated application rather than execute a particular program. This depends on running operating system. Lazarus provides a platform independent procedure **OpenDocument** which will handle it for you. Your application will continue running without waiting for the document process to close.
941947
942948``` pascal
943949uses LCLIntf;
944950...
945951OpenDocument('manual.pdf');
946952...
947953```
948954
949955- [[opendocument|OpenDocument reference]]
950956
951957### Open web page in default web browser
952958
953959Just pass the URL required, the leading <http://> appears to be optional under certain circumstances. Also, passing a filename appears to give the same results as OpenDocument()
954960
955961``` pascal
956962uses LCLIntf;
957963...
958964OpenURL('www.lazarus.freepascal.org/');
959965```
960966
961967See also:
962968
963969- [[OpenURL|OpenURL reference]]
964970
965971Or, you could use **TProcess** like this:
966972
967973``` pascal
968974uses Process;
969975
970976procedure OpenWebPage(URL: string);
971977// Apparently you need to pass your URL inside ", like "www.lazarus.freepascal.org"
972978var
973979 Browser, Params: string;
974980begin
975981 FindDefaultBrowser(Browser, Params);
976982 with TProcess.Create(nil) do
977983 try
978984 Executable := Browser;
979985 Params:=Format(Params, [URL]);
980986 Params:=copy(Params,2,length(Params)-2); // remove "", the new version of TProcess.Parameters does that itself
981987 Parameters.Add(Params);
982988 Options := [poNoConsole];
983989 Execute;
984990 finally
985991 Free;
986992 end;
987993end;
988994```
989995
990996## WinExec
991997
992998The Windows 3.x precursor of CreateProcess, sometimes still encountered in ancient Delphi code. Replace with one of the above. It is still available in unit Windows, but marked deprecated
993999
9941000## See also
9951001
9961002- [TProcess documentation](http://lazarus-ccr.sourceforge.net/docs/fcl/process/tprocess.html)
9971003- [[opendocument|OpenDocument]]
9981004- [[OpenURL]]
9991005- [[TProcessUTF8]]
10001006- [[TXMLPropStorage]]
10011007- [[Webbrowser]]
10021008
10031009---
10041010*From the Free Pascal wiki, page "Executing External Programs", as it stood on 2023-09-19 after a change by Marcov, 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