Sign in Join
Wiki copytemporary

Executing External Programs: what changed

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

OldNewLine
11## Overview: Comparison
22
3-Here are different ways available in RTL, FCL and LCL libraries on how to execute an external command/process/program.
4-
5-| Method | Library | Platforms | Single Line | Features |
6-|----|----|----|----|----|
7-| [[Executing External Programs|ExecuteProcess]] | RTL | Cross-Platform | Yes | Very limited, synchronous. |
8-| [[Executing External Programs|ShellExecute]] | WinAPI | MS Windows only | Yes | Many. Can start programs with elevation/admin permissions. |
9-| [[Executing External Programs|fpsystem, fpexecve]] | Unix | Unix only | | |
10-| [[Executing External Programs|TProcess]] | FCL | Cross-Platform | No | Full. |
11-| [[Executing External Programs|RunCommand]] | FCL | Cross-Platform **Requires FPC 2.6.2+** | Yes | Covers common TProcess usage. |
12-| [[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. |
13-| [[Executing External Programs|WinExec]] | WinAPI | MS Windows only | Yes Legacy option found in old Delphi code. Replace with one of the above. | |
3+Here are different ways available in RTL, FCL and LCL libraries on how to execute an external command/process/program.
144
5+Method | Library | Platforms | Single Line | Features
6+---|---|---|---|---
7+[[Executing External Programs|ExecuteProcess]] | RTL | Cross-Platform | Yes | Very limited, synchronous.
8+[[Executing External Programs|ShellExecute]] | WinAPI | MS Windows only | Yes | Many. Can start programs with elevation/admin permissions.
9+[[Executing External Programs|fpsystem, fpexecve]] | Unix | Unix only | |
10+[[Executing External Programs|TProcess]] | FCL | Cross-Platform | No | Full.
11+[[Executing External Programs|RunCommand]]RunCommand> "Executing External Programs") | FCL | Cross-Platform **Requires FPC 2.6.2+** | Yes | Covers common TProcess usage.
12+[[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.
13+[[Executing External Programs|WinExec]] | WinAPI | MS Windows only | Yes Legacy option found in old Delphi code. Replace with one of the above.
14+
1515## (Process.)RunCommand
1616
17-- [RunCommand](http://www.freepascal.org/docs-html/fcl/process/runcommand.html) reference
18-- [RunCommandInDir](http://www.freepascal.org/docs-html/fcl/process/runcommandindir.html) reference
19-
20-In 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.
17+ * [RunCommand](<http://www.freepascal.org/docs-html/fcl/process/runcommand.html>) reference
18+ * [RunCommandInDir](<http://www.freepascal.org/docs-html/fcl/process/runcommandindir.html>) reference
2119
22-A simple example is:
20+In 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.
2321
24-``` pascal
22+A simple example is:
23+```
2524program project1;
26-
27-{$mode objfpc}{$H+}
28-
29-uses
30- Process;
31-
32-var
33- s : ansistring;
34-
35-begin
36-
37-if RunCommand('/bin/bash',['-c','echo $PATH'],s) then
38- writeln(s);
39-
40-end.
25+
26+ {$mode objfpc}{$H+}
27+
28+ uses
29+ Process;
30+
31+ var
32+ s : ansistring;
33+
34+ begin
35+
36+ if RunCommand('/bin/bash',['-c','echo $PATH'],s) then
37+ writeln(s);
38+
39+ end.
4140```
4241
43-But 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:
44-
45-``` pascal
42+But 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:
43+```
4644program project2;
47-
48-{$mode objfpc}{$H+}
49-
50-uses
51- Process;
52-
53-var
54- s : ansistring;
55-
56-begin
57-
58-if RunCommand('/bin/bash',['-c','alias'],s) then
59- writeln(s);
60-
61-end.
45+
46+ {$mode objfpc}{$H+}
47+
48+ uses
49+ Process;
50+
51+ var
52+ s : ansistring;
53+
54+ begin
55+
56+ if RunCommand('/bin/bash',['-c','alias'],s) then
57+ writeln(s);
58+
59+ end.
6260```
6361
64-An overloaded variant of RunCommand returns the exitcode of the program. The RunCommandInDir runs the command in a different directory (sets p.CurrentDirectory):
65-
66-``` pascal
62+An overloaded variant of RunCommand returns the exitcode of the program. The RunCommandInDir runs the command in a different directory (sets p.CurrentDirectory):
63+```
6764function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string; var exitstatus:integer): integer;
68-function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string): boolean;
69-function RunCommand(const exename:string;const commands:array of string;var outputstring:string): boolean;
65+ function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string): boolean;
66+ function RunCommand(const exename:string;const commands:array of string;var outputstring:string): boolean;
7067```
7168
72-In **FPC 3.2.0+** the Runcommand got additional variants that allow to override TProcessOptions and WindowOptions.
73-
74-### RunCommand extensions
75-
76-In **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:
69+In **FPC 3.2.0+** the Runcommand got additional variants that allow to override TProcessOptions and WindowOptions.
7770
78-``` pascal
7971
80-program TestTProcessTimeout;
8172
82-uses classes, sysutils, process, dateutils;
83-
84-type
85- { TProcessTimeout }
86- TProcessTimeout = class(TProcess)
87- protected
88- timeoutperiod: TTime;
89- timedout : boolean;
90- started : TDateTime;
91- procedure LocalnIdleSleep(Sender,Context : TObject;status:TRunCommandEventCode;const message:string);
92- public
93- 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;
94- end;
95-
96-procedure TProcessTimeout.LocalnIdleSleep(Sender,Context : TObject;status:TRunCommandEventCode;const message:string);
97-begin
98- if status=RunCommandIdle then
99- begin
100- writeln(Executable+'('+ Parameters.CommaText+')' +' time: '+TimeToStr(now-started));
101- if (now-started)>timeoutperiod then
102- begin
103- writeln('process timed out ');
104- timedout:=true;
105- Terminate(255);
106- exit;
107- end;
108- sleep(RunCommandSleepTime);
109- end;
110-end;
111-
112-class function TProcessTimeout.RunCommandwithTimeout(
113- const exename : TProcessString;
114- const commands : array of TProcessString;
115- const dir : string;
116- out outputstring : string;
117- out errorstring : string;
118- out exitstate : integer;
119- ProcessOptions : TProcessOptions = [];
120- SWOptions : TShowWindowOptions=swoNone;
121- timeout : integer=15
122- ):boolean;
123-Var
124- p : TProcessTimeout;
125- i : integer;
126-begin
127- p:=TProcessTimeout.create(nil);
128- p.OnRunCommandEvent:=@p.LocalnIdleSleep;
129- p.CurrentDirectory:=dir;
130-
131- //timeout in Minutes, Check every Seconds
132- p.timeoutperiod:=timeout/MinsPerDay;
133-
134- (*
135- //if you want to do the Timeout in Seconds
136- p.timeoutperiod:=timeout/SecsPerDay;
137- *)
138-
139- // check every Second if finished
140- p.runcommandsleeptime := 1000;
141-
142- if ProcessOptions<>[] then
143- P.Options:=ProcessOptions - [poRunSuspended,poWaitOnExit];
144- p.options:=p.options+[poRunIdle]; // needed to run the RUNIDLE event. See User Changes 3.2.0
145-
146- P.ShowWindow:=SwOptions;
147- p.Executable:=exename;
148- if high(commands)>=0 then
149- for i:=low(commands) to high(commands) do
150- p.Parameters.add(commands[i]);
151- p.timedout:=false;
152- p.started:=now;
153- try
154- // the core loop of runcommand() variants, originally based on the "large output" scenario in the wiki, but continously expanded over 5 years.
155- RunCommandwithTimeout:=p.RunCommandLoop(outputstring,errorstring,exitstate)=0;
156- if p.timedout then
157- RunCommandwithTimeout:=false;
158- finally
159- p.free;
160- end;
161- if exitstate<>0 then RunCommandwithTimeout:=false;
162-end;
163-
164-// example use:
73+### RunCommand extensions
16574
166-var
167- output,errors : string;
168- exitcode :integer;
169-begin
170- //Windows and Unix have other programs that can be used
171- {$if defined(WINDOWS)}
172- if TProcessTimeout.RunCommandwithTimeout('cmd',['/c','dir','C:*.exe','/s'],'c:\',output,errors,exitcode,[],swoNone,1) then // dir c:*.exe /s
173- {$else$}
174- if TProcessTimeout.RunCommandwithTimeout('find',['/','-name','"*.sh"'],'/',output,errors,exitcode,[],swoNone,1) then // find / -name "*.sh"
175- {$endif}
176- begin
177- writeln('program finished');
178- writeln('--------------------output-------------------');
179- writeln(output);
180- writeln('--------------------errors-------------------');
181- writeln(errors);
182- if not(exitcode=0) then
75+In **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:
76+```
77+program TestTProcessTimeout;
78+
79+ uses classes, sysutils, process, dateutils;
80+
81+ type
82+ { TProcessTimeout }
83+ TProcessTimeout = class(TProcess)
84+ protected
85+ timeoutperiod: TTime;
86+ timedout : boolean;
87+ started : TDateTime;
88+ procedure LocalnIdleSleep(Sender,Context : TObject;status:TRunCommandEventCode;const message:string);
89+ public
90+ 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;
91+ end;
92+
93+
94+
95+ procedure TProcessTimeout.LocalnIdleSleep(Sender,Context : TObject;status:TRunCommandEventCode;const message:string);
18396 begin
184- writeln('-program finished with errors (exitcode: '+exitcode.tostring+')-');
97+ if status=RunCommandIdle then
98+ begin
99+ writeln(Executable+'('+ Parameters.CommaText+')' +' time: '+TimeToStr(now-started));
100+ if (now-started)>timeoutperiod then
101+ begin
102+ writeln('process timed out ');
103+ timedout:=true;
104+ Terminate(255);
105+ exit;
106+ end;
107+ sleep(RunCommandSleepTime);
108+ end;
185109 end;
186- readln();
187- end
188- else
189- begin
190- writeln('program not finished');
191- writeln('--------------------output-------------------');
192- writeln(output);
193- writeln('--------------------errors-------------------');
194- writeln(errors);
195- writeln('-------------------EXITCODE------------------');
196- writeln(exitcode);
197- readln();
198- end;
199-end.
110+
111+ class function TProcessTimeout.RunCommandwithTimeout(
112+ const exename : TProcessString;
113+ const commands : array of TProcessString;
114+ const dir : string;
115+ out outputstring : string;
116+ out errorstring : string;
117+ out exitstate : integer;
118+ ProcessOptions : TProcessOptions = [];
119+ SWOptions : TShowWindowOptions=swoNone;
120+ timeout : integer=15
121+ ):boolean;
122+ Var
123+ p : TProcessTimeout;
124+ i : integer;
125+ begin
126+ p:=TProcessTimeout.create(nil);
127+ p.OnRunCommandEvent:=@p.LocalnIdleSleep;
128+ p.CurrentDirectory:=dir;
129+
130+
131+ //timeout in Minutes, Check every Seconds
132+ p.timeoutperiod:=timeout/MinsPerDay;
133+
134+ (*
135+ //if you want to do the Timeout in Seconds
136+ p.timeoutperiod:=timeout/SecsPerDay;
137+ *)
138+
139+ // check every Second if finished
140+ p.runcommandsleeptime := 1000;
141+
142+ if ProcessOptions<>[] then
143+ P.Options:=ProcessOptions - [poRunSuspended,poWaitOnExit];
144+ p.options:=p.options+[poRunIdle]; // needed to run the RUNIDLE event. See User Changes 3.2.0
145+
146+ P.ShowWindow:=SwOptions;
147+ p.Executable:=exename;
148+ if high(commands)>=0 then
149+ for i:=low(commands) to high(commands) do
150+ p.Parameters.add(commands[i]);
151+ p.timedout:=false;
152+ p.started:=now;
153+ try
154+ // the core loop of runcommand() variants, originally based on the "large output" scenario in the wiki, but continously expanded over 5 years.
155+ RunCommandwithTimeout:=p.RunCommandLoop(outputstring,errorstring,exitstate)=0;
156+ if p.timedout then
157+ RunCommandwithTimeout:=false;
158+ finally
159+ p.free;
160+ end;
161+ if exitstate<>0 then RunCommandwithTimeout:=false;
162+ end;
163+
164+ // example use:
165+
166+ var
167+ output,errors : string;
168+ exitcode :integer;
169+ begin
170+ //Windows and Unix have other programs that can be used
171+ {$if defined(WINDOWS)}
172+ if TProcessTimeout.RunCommandwithTimeout('cmd',['/c','dir','C:\*.exe','/s'],'c:\',output,errors,exitcode,[],swoNone,1) then // dir c:\*.exe /s
173+ {$else$}
174+ if TProcessTimeout.RunCommandwithTimeout('find',['/','-name','"*.sh"'],'/',output,errors,exitcode,[],swoNone,1) then // find / -name "*.sh"
175+ {$endif}
176+ begin
177+ writeln('program finished');
178+ writeln('--------------------output-------------------');
179+ writeln(output);
180+ writeln('--------------------errors-------------------');
181+ writeln(errors);
182+ if not(exitcode=0) then
183+ begin
184+ writeln('-program finished with errors (exitcode: '+exitcode.tostring+')-');
185+ end;
186+ readln();
187+ end
188+ else
189+ begin
190+ writeln('program not finished');
191+ writeln('--------------------output-------------------');
192+ writeln(output);
193+ writeln('--------------------errors-------------------');
194+ writeln(errors);
195+ writeln('-------------------EXITCODE------------------');
196+ writeln(exitcode);
197+ readln();
198+ end;
199+ end.
200200```
201201
202202## SysUtils.ExecuteProcess
203203
204-(Cross-platform)\
205-Despite a number of limitations, the simplest way to launch a program (modal, no pipes or any form of control) is to simply use :
206-
207-``` pascal
204+(Cross-platform)
205+Despite a number of limitations, the simplest way to launch a program (modal, no pipes or any form of control) is to simply use :
206+```
208207SysUtils.ExecuteProcess(UTF8ToSys('/full/path/to/binary'), '', []);
209208```
210209
211-The 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**.
210+The 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**.
212211
213-- [ExecuteProcess reference](http://www.freepascal.org/docs-html/rtl/sysutils/executeprocess.html)
212+ * [ExecuteProcess reference](<http://www.freepascal.org/docs-html/rtl/sysutils/executeprocess.html>)
214213
215214## MS Windows: CreateProcess, ShellExecute and WinExec
216215
217-> **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**.
218-
219-> **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.
216+![Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png) **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**.
220217
221-**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).
218+![Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png) **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.
222219
223-``` pascal
220+**ShellExecute** is a standard MS Windows function (ShellApi.h) with good [documentation on MSDN](<http://msdn.microsoft.com/en-us/library/windows/desktop/bb762153\(v=vs.85\).aspx>) (note their remarks about initialising COM if you find the function unreliable).
221+```
224222uses ..., ShellApi;
225-
226-// Simple one-liner (ignoring error returns) :
227-if ShellExecute(0,nil, PChar('"C:\my dir\prog.exe"'),PChar('"C:\somepath\some_doc.ext"'),nil,1) =0 then;
228-
229-// Execute a Batch File :
230-if ShellExecute(0,nil, PChar('cmd'),PChar('/c mybatch.bat'),nil,1) =0 then;
231-
232-// Open a command window in a given folder :
233-if ShellExecute(0,nil, PChar('cmd'),PChar('/k cd \path'),nil,1) =0 then;
234-
235-// Open a webpage URL in the default browser using 'start' command (via a brief hidden cmd window) :
236-if ShellExecute(0,nil, PChar('cmd'),PChar('/c start www.lazarus.freepascal.org/'),nil,0) =0 then;
237-
238-// or a useful procedure:
239-procedure RunShellExecute(const prog,params:string);
240-begin
241- // ( Handle, nil/'open'/'edit'/'find'/'explore'/'print', // 'open' isn't always needed
242- // path+prog, params, working folder,
243- // 0=hide / 1=SW_SHOWNORMAL / 3=max / 7=min) // for SW_ constants : uses ... Windows ...
244- if ShellExecute(0,'open',PChar(prog),PChar(params),PChar(extractfilepath(prog)),1) >32 then; //success
245- // return values 0..32 are errors
246-end;
223+
224+ // Simple one-liner (ignoring error returns) :
225+ if ShellExecute(0,nil, PChar('"C:\my dir\prog.exe"'),PChar('"C:\somepath\some_doc.ext"'),nil,1) =0 then;
226+
227+ // Execute a Batch File :
228+ if ShellExecute(0,nil, PChar('cmd'),PChar('/c mybatch.bat'),nil,1) =0 then;
229+
230+ // Open a command window in a given folder :
231+ if ShellExecute(0,nil, PChar('cmd'),PChar('/k cd \path'),nil,1) =0 then;
232+
233+ // Open a webpage URL in the default browser using 'start' command (via a brief hidden cmd window) :
234+ if ShellExecute(0,nil, PChar('cmd'),PChar('/c start www.lazarus.freepascal.org/'),nil,0) =0 then;
235+
236+ // or a useful procedure:
237+ procedure RunShellExecute(const prog,params:string);
238+ begin
239+ // ( Handle, nil/'open'/'edit'/'find'/'explore'/'print', // 'open' isn't always needed
240+ // path+prog, params, working folder,
241+ // 0=hide / 1=SW_SHOWNORMAL / 3=max / 7=min) // for SW_ constants : uses ... Windows ...
242+ if ShellExecute(0,'open',PChar(prog),PChar(params),PChar(extractfilepath(prog)),1) >32 then; //success
243+ // return values 0..32 are errors
244+ end;
247245```
248246
249-There is also ShellExecuteExW as a WideChar version, and ShellExecuteExA is AnsiChar.
247+There is also ShellExecuteExW as a WideChar version, and ShellExecuteExA is AnsiChar.
250248
251-The fMask option can also use SEE_MASK_DOENVSUBST or SEE_MASK_FLAG_NO_UI or SEE_MASK_NOCLOSEPROCESS, etc.
249+The fMask option can also use SEE_MASK_DOENVSUBST or SEE_MASK_FLAG_NO_UI or SEE_MASK_NOCLOSEPROCESS, etc.
252250
253-If 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).
251+If 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).
254252
255253### Using ShellExecuteEx for elevation/administrator permissions
256254
257-If you need to execute external program with administrator/elevated privileges, you can use the **runas** method with the alternative ShellExecuteEx function:
258-
259-``` pascal
255+If you need to execute external program with administrator/elevated privileges, you can use the **runas** method with the alternative ShellExecuteEx function:
256+```
260257uses ShellApi, ...;
261-
262-function RunAsAdmin(const Handle: Hwnd; const Path, Params: string): Boolean;
263-var
264- sei: TShellExecuteInfoA;
265-begin
266- FillChar(sei, SizeOf(sei), 0);
267- sei.cbSize := SizeOf(sei);
268- sei.Wnd := Handle;
269- sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
270- sei.lpVerb := 'runas';
271- sei.lpFile := PAnsiChar(Path);
272- sei.lpParameters := PAnsiChar(Params);
273- sei.nShow := SW_SHOWNORMAL;
274- Result := ShellExecuteExA(@sei);
275-end;
276-
277-procedure TFormMain.RunAddOrRemoveApplication;
278-begin
279- // Example that uses elevated rundll to open the Control Panel to Programs and features
280- RunAsAdmin(FormMain.Handle, 'rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl', '');
281-end;
258+
259+ function RunAsAdmin(const Handle: Hwnd; const Path, Params: string): Boolean;
260+ var
261+ sei: TShellExecuteInfoA;
262+ begin
263+ FillChar(sei, SizeOf(sei), 0);
264+ sei.cbSize := SizeOf(sei);
265+ sei.Wnd := Handle;
266+ sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
267+ sei.lpVerb := 'runas';
268+ sei.lpFile := PAnsiChar(Path);
269+ sei.lpParameters := PAnsiChar(Params);
270+ sei.nShow := SW_SHOWNORMAL;
271+ Result := ShellExecuteExA(@sei);
272+ end;
273+
274+ procedure TFormMain.RunAddOrRemoveApplication;
275+ begin
276+ // Example that uses elevated rundll to open the Control Panel to Programs and features
277+ RunAsAdmin(FormMain.Handle, 'rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl', '');
278+ end;
282279```
283280
284281## Unix fpsystem, fpexecve and shell
285282
286-These functions are platform dependent.
283+These functions are platform dependent.
287284
288-- [fpsystem reference](http://www.freepascal.org/docs-html/rtl/unix/fpsystem.html)
289-- [fpexecve reference](http://www.freepascal.org/docs-html/rtl/baseunix/fpexecve.html)
285+ * [fpsystem reference](<http://www.freepascal.org/docs-html/rtl/unix/fpsystem.html>)
286+ * [fpexecve reference](<http://www.freepascal.org/docs-html/rtl/baseunix/fpexecve.html>)
290287
291-Linux.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.
288+Linux.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.
292289
293290## TProcess
294291
295-You can use TProcess to launch external programs. Some of the benefits of using TProcess are that it is:
292+You can use TProcess to launch external programs. Some of the benefits of using TProcess are that it is:
296293
297-- Platform Independent
298-- Capable of reading from stdout and writing to stdin.
299-- Possible to wait for a command to finish or let it run while your program moves on.
294+ * Platform Independent
295+ * Capable of reading from stdout and writing to stdin.
296+ * Possible to wait for a command to finish or let it run while your program moves on.
300297
301-Important notes:
298+Important notes:
302299
303-- 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..
304-- 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.
305-- On Windows, if the command is in the path, you don't need to specify the full path.
306-- [[doc:fcl/process/tprocess.html|TProcess reference]]
300+ * 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..
301+ * 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 [FindDefaultExecutablePath](<http://lazarus-ccr.sourceforge.net/docs/lcl/fileutil/finddefaultexecutablepath.html> "doc:lcl/fileutil/finddefaultexecutablepath.html") from the [FileUtil](<http://lazarus-ccr.sourceforge.net/docs/lcl/fileutil/index.html> "doc:lcl/fileutil/index.html") unit of the LCL.
302+ * On Windows, if the command is in the path, you don't need to specify the full path.
303+ * [TProcess reference](<http://lazarus-ccr.sourceforge.net/docs/fcl/process/tprocess.html> "doc:fcl/process/tprocess.html")
307304
308305### The Simplest Example
309306
310-A 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.
307+A 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.
311308
312309### A Simple Example
313310
314-This 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:
315-
316-``` pascal
311+This 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:
312+```
317313// This is a demo program that shows
318-// how to launch an external program.
319-program launchprogram;
320-
321-// Here we include files that have useful functions
322-// and procedures we will need.
323-uses
324- Classes, SysUtils, Process;
325-
326-// This defines the var "AProcess" as a variable
327-// of the type "TProcess"
328-var
329- AProcess: TProcess;
330-
331-// This is where our program starts to run
332-begin
333- // Now we will create the TProcess object, and
334- // assign it to the var AProcess.
335- AProcess := TProcess.Create(nil);
336-
337- // Tell the new AProcess what the command to execute is.
338- // Let's use the Free Pascal compiler (i386 version that is)
339- AProcess.Executable:= 'ppc386';
340-
341- // Pass -h together with ppc386 so actually 'ppc386 -h' is executed:
342- AProcess.Parameters.Add('-h');
343-
344- // We will define an option for when the program
345- // is run. This option will make sure that our program
346- // does not continue until the program we will launch
347- // has stopped running. vvvvvvvvvvvvvv
348- AProcess.Options := AProcess.Options + [poWaitOnExit];
349-
350- // Now let AProcess run the program
351- AProcess.Execute;
352-
353- // This is not reached until ppc386 stops running.
354- AProcess.Free;
355-end.
314+ // how to launch an external program.
315+ program launchprogram;
316+
317+ // Here we include files that have useful functions
318+ // and procedures we will need.
319+ uses
320+ Classes, SysUtils, Process;
321+
322+ // This defines the var "AProcess" as a variable
323+ // of the type "TProcess"
324+ var
325+ AProcess: TProcess;
326+
327+ // This is where our program starts to run
328+ begin
329+ // Now we will create the TProcess object, and
330+ // assign it to the var AProcess.
331+ AProcess := TProcess.Create(nil);
332+
333+ // Tell the new AProcess what the command to execute is.
334+ // Let's use the Free Pascal compiler (i386 version that is)
335+ AProcess.Executable:= 'ppc386';
336+
337+ // Pass -h together with ppc386 so actually 'ppc386 -h' is executed:
338+ AProcess.Parameters.Add('-h');
339+
340+ // We will define an option for when the program
341+ // is run. This option will make sure that our program
342+ // does not continue until the program we will launch
343+ // has stopped running. vvvvvvvvvvvvvv
344+ AProcess.Options := AProcess.Options + [poWaitOnExit];
345+
346+ // Now let AProcess run the program
347+ AProcess.Execute;
348+
349+ // This is not reached until ppc386 stops running.
350+ AProcess.Free;
351+ end.
356352```
357353
358-That's it! You have just learned to run an external program from inside your own program.
354+That's it! You have just learned to run an external program from inside your own program.
359355
360356### An improved example (but not correct yet)
361357
362-That's nice, but how do I read the Output of a program that I have run?
363-
364-Well, 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.**
358+That's nice, but how do I read the Output of a program that I have run?
365359
366-``` pascal
360+Well, 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.**
361+```
367362// This is a
368-// FLAWED
369-// demo program that shows
370-// how to launch an external program
371-// and read from its output.
372-program launchprogram;
373-
374-// Here we include files that have useful functions
375-// and procedures we will need.
376-uses
377- Classes, SysUtils, Process;
378-
379-// This is defining the var "AProcess" as a variable
380-// of the type "TProcess"
381-// Also now we are adding a TStringList to store the
382-// data read from the programs output.
383-var
384- AProcess: TProcess;
385- AStringList: TStringList;
386-
387-// This is where our program starts to run
388-begin
389- // Now we will create the TProcess object, and
390- // assign it to the var AProcess.
391- AProcess := TProcess.Create(nil);
392-
393- // Tell the new AProcess what the command to execute is.
394- AProcess.Executable := '/usr/bin/ppc386';
395- AProcess.Parameters.Add('-h');
396-
397- // We will define an option for when the program
398- // is run. This option will make sure that our program
399- // does not continue until the program we will launch
400- // has stopped running. Also now we will tell it that
401- // we want to read the output of the file.
402- AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
403-
404- // Now that AProcess knows what the commandline is it can be run.
405- AProcess.Execute;
406-
407- // After AProcess has finished, the rest of the program will be executed.
408-
409- // Now read the output of the program we just ran into a TStringList.
410- AStringList := TStringList.Create;
411- AStringList.LoadFromStream(AProcess.Output);
412-
413- // Save the output to a file and clean up the TStringList.
414- AStringList.SaveToFile('output.txt');
415- AStringList.Free;
416-
417- // Now that the output from the process is processed, it can be freed.
418- AProcess.Free;
419-end.
363+ // FLAWED
364+ // demo program that shows
365+ // how to launch an external program
366+ // and read from its output.
367+ program launchprogram;
368+
369+ // Here we include files that have useful functions
370+ // and procedures we will need.
371+ uses
372+ Classes, SysUtils, Process;
373+
374+ // This is defining the var "AProcess" as a variable
375+ // of the type "TProcess"
376+ // Also now we are adding a TStringList to store the
377+ // data read from the programs output.
378+ var
379+ AProcess: TProcess;
380+ AStringList: TStringList;
381+
382+ // This is where our program starts to run
383+ begin
384+ // Now we will create the TProcess object, and
385+ // assign it to the var AProcess.
386+ AProcess := TProcess.Create(nil);
387+
388+ // Tell the new AProcess what the command to execute is.
389+ AProcess.Executable := '/usr/bin/ppc386';
390+ AProcess.Parameters.Add('-h');
391+
392+ // We will define an option for when the program
393+ // is run. This option will make sure that our program
394+ // does not continue until the program we will launch
395+ // has stopped running. Also now we will tell it that
396+ // we want to read the output of the file.
397+ AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
398+
399+ // Now that AProcess knows what the commandline is it can be run.
400+ AProcess.Execute;
401+
402+ // After AProcess has finished, the rest of the program will be executed.
403+
404+ // Now read the output of the program we just ran into a TStringList.
405+ AStringList := TStringList.Create;
406+ AStringList.LoadFromStream(AProcess.Output);
407+
408+ // Save the output to a file and clean up the TStringList.
409+ AStringList.SaveToFile('output.txt');
410+ AStringList.Free;
411+
412+ // Now that the output from the process is processed, it can be freed.
413+ AProcess.Free;
414+ end.
420415```
421416
422417### Reading large output
423418
424-In the previous example we waited until the program exited. Then we read what the program has written to its output.
425-
426-Suppose 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.
419+In the previous example we waited until the program exited. Then we read what the program has written to its output.
427420
428-But the calling program doesn't read from it until the called program has ended. A deadlock occurs.
421+Suppose 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.
429422
430-The 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.
423+But the calling program doesn't read from it until the called program has ended. A deadlock occurs.
431424
432-If 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.
425+The 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.
433426
434-``` pascal
427+If 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.
428+```
435429program LargeOutputDemo;
436-
437-{$mode objfpc}{$H+}
438-
439-uses
440- Classes, SysUtils, Process; // Process is the unit that holds TProcess
441-
442-const
443- BUF_SIZE = 2048; // Buffer size for reading the output in chunks
444-
445-var
446- AProcess : TProcess;
447- OutputStream : TStream;
448- BytesRead : longint;
449- Buffer : array[1..BUF_SIZE] of byte;
450-
451-begin
452- // Set up the process; as an example a recursive directory search is used
453- // because that will usually result in a lot of data.
454- AProcess := TProcess.Create(nil);
455-
456- // The commands for Windows and *nix are different hence the $IFDEFs
457- {$IFDEF Windows}
458- // In Windows the dir command cannot be used directly because it's a build-in
459- // shell command. Therefore cmd.exe and the extra parameters are needed.
460- AProcess.Executable := 'c:\windows\system32\cmd.exe';
461- AProcess.Parameters.Add('/c');
462- AProcess.Parameters.Add('dir /s c:\windows');
463- {$ENDIF Windows}
464-
465- {$IFDEF Unix}
466- AProcess.Executable := '/bin/ls';
467-
468- {$IFDEF Darwin}
469- AProcess.Parameters.Add('-recursive');
470- AProcess.Parameters.Add('-all');
471- {$ENDIF Darwin}
472-
473- {$IFDEF Linux}
474- AProcess.Parameters.Add('--recursive');
475- AProcess.Parameters.Add('--all');
476- {$ENDIF Linux}
477-
478- {$IFDEF FreeBSD}
479- AProcess.Parameters.Add('-R');
480- AProcess.Parameters.Add('-a');
481- {$ENDIF FreeBSD}
482-
483- AProcess.Parameters.Add('-l');
484- {$ENDIF Unix}
485-
486- // Process option poUsePipes has to be used so the output can be captured.
487- // Process option poWaitOnExit can not be used because that would block
488- // this program, preventing it from reading the output data of the process.
489- AProcess.Options := [poUsePipes];
490-
491- // Start the process (run the dir/ls command)
492- AProcess.Execute;
493-
494- // Create a stream object to store the generated output in. This could
495- // also be a file stream to directly save the output to disk.
496- OutputStream := TMemoryStream.Create;
497-
498- // All generated output from AProcess is read in a loop until no more data is available
499- repeat
500- // Get the new data from the process to a maximum of the buffer size that was allocated.
501- // Note that all read(...) calls will block except for the last one, which returns 0 (zero).
502- BytesRead := AProcess.Output.Read(Buffer, BUF_SIZE);
503-
504- // Add the bytes that were read to the stream for later usage
505- OutputStream.Write(Buffer, BytesRead)
506-
507- until BytesRead = 0; // Stop if no more data is available
508-
509- // The process has finished so it can be cleaned up
510- AProcess.Free;
511-
512- // Now that all data has been read it can be used; for example to save it to a file on disk
513- with TFileStream.Create('output.txt', fmCreate) do
514- begin
515- OutputStream.Position := 0; // Required to make sure all data is copied from the start
516- CopyFrom(OutputStream, OutputStream.Size);
517- Free
518- end;
519-
520- // Or the data can be shown on screen
521- with TStringList.Create do
522- begin
523- OutputStream.Position := 0; // Required to make sure all data is copied from the start
524- LoadFromStream(OutputStream);
525- writeln(Text);
526- writeln('--- Number of lines = ', Count, '----');
527- Free
528- end;
529-
530- // Clean up
531- OutputStream.Free;
532-end.
430+
431+ {$mode objfpc}{$H+}
432+
433+ uses
434+ Classes, SysUtils, Process; // Process is the unit that holds TProcess
435+
436+ const
437+ BUF_SIZE = 2048; // Buffer size for reading the output in chunks
438+
439+ var
440+ AProcess : TProcess;
441+ OutputStream : TStream;
442+ BytesRead : longint;
443+ Buffer : array[1..BUF_SIZE] of byte;
444+
445+ begin
446+ // Set up the process; as an example a recursive directory search is used
447+ // because that will usually result in a lot of data.
448+ AProcess := TProcess.Create(nil);
449+
450+ // The commands for Windows and *nix are different hence the $IFDEFs
451+ {$IFDEF Windows}
452+ // In Windows the dir command cannot be used directly because it's a build-in
453+ // shell command. Therefore cmd.exe and the extra parameters are needed.
454+ AProcess.Executable := 'c:\windows\system32\cmd.exe';
455+ AProcess.Parameters.Add('/c');
456+ AProcess.Parameters.Add('dir /s c:\windows');
457+ {$ENDIF Windows}
458+
459+ {$IFDEF Unix}
460+ AProcess.Executable := '/bin/ls';
461+
462+ {$IFDEF Darwin}
463+ AProcess.Parameters.Add('-recursive');
464+ AProcess.Parameters.Add('-all');
465+ {$ENDIF Darwin}
466+
467+ {$IFDEF Linux}
468+ AProcess.Parameters.Add('--recursive');
469+ AProcess.Parameters.Add('--all');
470+ {$ENDIF Linux}
471+
472+ {$IFDEF FreeBSD}
473+ AProcess.Parameters.Add('-R');
474+ AProcess.Parameters.Add('-a');
475+ {$ENDIF FreeBSD}
476+
477+ AProcess.Parameters.Add('-l');
478+ {$ENDIF Unix}
479+
480+ // Process option poUsePipes has to be used so the output can be captured.
481+ // Process option poWaitOnExit can not be used because that would block
482+ // this program, preventing it from reading the output data of the process.
483+ AProcess.Options := [poUsePipes];
484+
485+ // Start the process (run the dir/ls command)
486+ AProcess.Execute;
487+
488+ // Create a stream object to store the generated output in. This could
489+ // also be a file stream to directly save the output to disk.
490+ OutputStream := TMemoryStream.Create;
491+
492+ // All generated output from AProcess is read in a loop until no more data is available
493+ repeat
494+ // Get the new data from the process to a maximum of the buffer size that was allocated.
495+ // Note that all read(...) calls will block except for the last one, which returns 0 (zero).
496+ BytesRead := AProcess.Output.Read(Buffer, BUF_SIZE);
497+
498+ // Add the bytes that were read to the stream for later usage
499+ OutputStream.Write(Buffer, BytesRead)
500+
501+ until BytesRead = 0; // Stop if no more data is available
502+
503+ // The process has finished so it can be cleaned up
504+ AProcess.Free;
505+
506+ // Now that all data has been read it can be used; for example to save it to a file on disk
507+ with TFileStream.Create('output.txt', fmCreate) do
508+ begin
509+ OutputStream.Position := 0; // Required to make sure all data is copied from the start
510+ CopyFrom(OutputStream, OutputStream.Size);
511+ Free
512+ end;
513+
514+ // Or the data can be shown on screen
515+ with TStringList.Create do
516+ begin
517+ OutputStream.Position := 0; // Required to make sure all data is copied from the start
518+ LoadFromStream(OutputStream);
519+ writeln(Text);
520+ writeln('--- Number of lines = ', Count, '----');
521+ Free
522+ end;
523+
524+ // Clean up
525+ OutputStream.Free;
526+ end.
533527```
534528
535-Note that the above could also be accomplished by using RunCommand:
536-
537-``` pascal
529+Note that the above could also be accomplished by using RunCommand:
530+```
538531var s: string;
539-...
540-RunCommand('c:\windows\system32\cmd.exe', ['/c', 'dir /s c:\windows'], s);
532+ ...
533+ RunCommand('c:\windows\system32\cmd.exe', ['/c', 'dir /s c:\windows'], s);
541534```
542535
543536### Using input and output of a TProcess
544537
545-See processdemo example in the [Lazarus-CCR SVN](https://sourceforge.net/p/lazarus-ccr/svn/HEAD/tree/examples/process).
538+See processdemo example in the [Lazarus-CCR SVN](<https://sourceforge.net/p/lazarus-ccr/svn/HEAD/tree/examples/process>).
546539
547540### Hints on the use of TProcess
548541
549-When creating a cross-platform program, the OS-specific executable name can be set using directives "{\$IFDEF}" and "{\$ENDIF}".
550-
551-Example:
552-
553-``` pascal
554-{...}
555-AProcess := TProcess.Create(nil)
556-
557-{$IFDEF WIN32}
558- AProcess.Executable := 'calc.exe';
559-{$ENDIF}
560-
561-{$IFDEF LINUX}
562- AProcess.Executable := FindDefaultExecutablePath('kcalc');
563-{$ENDIF}
542+When creating a cross-platform program, the OS-specific executable name can be set using directives "{$IFDEF}" and "{$ENDIF}".
564543
565-AProcess.Execute;
544+Example:
545+```
566546{...}
547+ AProcess := TProcess.Create(nil)
548+
549+ {$IFDEF WIN32}
550+ AProcess.Executable := 'calc.exe';
551+ {$ENDIF}
552+
553+ {$IFDEF LINUX}
554+ AProcess.Executable := FindDefaultExecutablePath('kcalc');
555+ {$ENDIF}
556+
557+ AProcess.Execute;
558+ {...}
567559```
568560
569561### macOS show application bundle in foreground
570562
571-You can start an **application bundle** via TProcess by starting the executable within the bundle. For example:
572-
573-``` pascal
574- AProcess.Executable:='/Applications/iCal.app/Contents/MacOS/iCal';
563+You can start an **application bundle** via TProcess by starting the executable within the bundle. For example:
575564```
576-
577-This 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:
578-
579-``` pascal
580- AProcess.Executable:='/usr/bin/open';
581- AProcess.Parameters.Add('-n');
582- AProcess.Parameters.Add('-a'); // optional: specifies the application to use; only searches the Application directories
583- AProcess.Parameters.Add('-W'); // optional: open waits until the applications it opens (or were already open) have exited
584- AProcess.Parameters.Add('Pages.app'); // including .app is optional
565+AProcess.Executable:='/Applications/iCal.app/Contents/MacOS/iCal';
585566```
586567
587-If your application needs parameters, you can pass **open** the **--args** parameter, after which all parameters are passed to the application:
568+This 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:
569+```
570+AProcess.Executable:='/usr/bin/open';
571+ AProcess.Parameters.Add('-n');
572+ AProcess.Parameters.Add('-a'); // optional: specifies the application to use; only searches the Application directories
573+ AProcess.Parameters.Add('-W'); // optional: open waits until the applications it opens (or were already open) have exited
574+ AProcess.Parameters.Add('Pages.app'); // including .app is optional
575+```
588576
589-``` pascal
590- AProcess.Parameters.Add('--args');
591- AProcess.Parameters.Add('argument1');
592- AProcess.Parameters.Add('argument2');
577+If your application needs parameters, you can pass **open** the **\--args** parameter, after which all parameters are passed to the application:
593578```
579+AProcess.Parameters.Add('--args');
580+ AProcess.Parameters.Add('argument1');
581+ AProcess.Parameters.Add('argument2');
582+```
594583
595-See also: [[macOS Open Sesame|macOS open command]].
584+See also: [[macOS Open Sesame|macOS open command]].
596585
597586### Run detached program
598587
599-Normally 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:
600-
601-``` pascal
588+Normally 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:
589+```
602590var
603- Process: TProcess;
604- I: Integer;
605-begin
606- Process := TProcess.Create(nil);
607- try
608- Process.InheritHandles := False;
609- Process.Options := [];
610- Process.ShowWindow := swoShow;
611-
612- // Copy default environment variables including DISPLAY variable for GUI application to work
613- for I := 1 to GetEnvironmentVariableCount do
614- Process.Environment.Add(GetEnvironmentString(I));
615-
616- Process.Executable := '/usr/bin/gedit';
617- Process.Execute;
618- finally
619- Process.Free;
620- end;
621-end;
591+ Process: TProcess;
592+ I: Integer;
593+ begin
594+ Process := TProcess.Create(nil);
595+ try
596+ Process.InheritHandles := False;
597+ Process.Options := [];
598+ Process.ShowWindow := swoShow;
599+
600+ // Copy default environment variables including DISPLAY variable for GUI application to work
601+ for I := 1 to GetEnvironmentVariableCount do
602+ Process.Environment.Add(GetEnvironmentString(I));
603+
604+ Process.Executable := '/usr/bin/gedit';
605+ Process.Execute;
606+ finally
607+ Process.Free;
608+ end;
609+ end;
622610```
623611
624612### Example of "talking" with aspell process
625613
626-Inside [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:
614+Inside [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:
627615
628-- [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.
616+ * [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.
629617
630-- [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.
618+ * [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.
631619
632-Both 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.
620+Both 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.
633621
634-### Replacing shell operators like "\| \< \>"
622+###
635623
636-Sometimes you want to run a more complicated command that pipes its data to another command or to a file. Something like
624+Replacing shell operators like "| < >"
637625
638-``` pascal
626+Sometimes you want to run a more complicated command that pipes its data to another command or to a file. Something like
627+```
639628ShellExecute('firstcommand.exe | secondcommand.exe');
640629```
641630
642-or
643-
644-``` pascal
631+or
632+```
645633ShellExecute('dir > output.txt');
646634```
647635
648-Executing this with TProcess will not work. i.e:
649-
650-``` pascal
636+Executing this with TProcess will not work. i.e:
637+```
651638// this won't work
652-Process.CommandLine := 'firstcommand.exe | secondcommand.exe';
653-Process.Execute;
639+ Process.CommandLine := 'firstcommand.exe | secondcommand.exe';
640+ Process.Execute;
654641```
655642
656643#### Why using special operators to redirect output doesn't work
657644
658-TProcess is just that, it's not a shell environment, only a process. It's not two processes, it's only one. It is possible to redirect output however just the way you wanted. See the [[Executing External Programs|next section]].
645+TProcess is just that, it's not a shell environment, only a process. It's not two processes, it's only one. It is possible to redirect output however just the way you wanted. See the [[Executing External Programs|next section]].
659646
660647### How to redirect output with TProcess
661648
662-You can redirect the output of a command to another command by using a TProcess instance for **each** command.
649+You can redirect the output of a command to another command by using a TProcess instance for **each** command.
663650
664651Here'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]]
665652
666-Not 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.
667-
668-``` pascal
653+Not 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.
654+```
669655program Project1;
670-
671-uses
672- Classes, sysutils, process;
673-
674-var
675- FirstProcess,
676- SecondProcess: TProcess;
677- Buffer: array[0..127] of char;
678- ReadCount: Integer;
679- ReadSize: Integer;
680-begin
681- FirstProcess := TProcess.Create(nil);
682- SecondProcess := TProcess.Create(nil);
683-
684- FirstProcess.Options := [poUsePipes];
685- FirstProcess.Executable := 'pwd';
686-
687- SecondProcess.Options := [poUsePipes,poStderrToOutPut];
688- SecondProcess.Executable := 'grep';
689- SecondProcess.Parameters.Add(DirectorySeparator+ ' -');
690- // this would be the same as "pwd | grep / -"
691-
692- FirstProcess.Execute;
693- SecondProcess.Execute;
694-
695- while FirstProcess.Running or (FirstProcess.Output.NumBytesAvailable > 0) do
696- begin
697- if FirstProcess.Output.NumBytesAvailable > 0 then
656+
657+ uses
658+ Classes, sysutils, process;
659+
660+ var
661+ FirstProcess,
662+ SecondProcess: TProcess;
663+ Buffer: array[0..127] of char;
664+ ReadCount: Integer;
665+ ReadSize: Integer;
698666 begin
699- // make sure that we don't read more data than we have allocated
700- // in the buffer
701- ReadSize := FirstProcess.Output.NumBytesAvailable;
667+ FirstProcess := TProcess.Create(nil);
668+ SecondProcess := TProcess.Create(nil);
669+
670+ FirstProcess.Options := [poUsePipes];
671+ FirstProcess.Executable := 'pwd';
672+
673+ SecondProcess.Options := [poUsePipes,poStderrToOutPut];
674+ SecondProcess.Executable := 'grep';
675+ SecondProcess.Parameters.Add(DirectorySeparator+ ' -');
676+ // this would be the same as "pwd | grep / -"
677+
678+ FirstProcess.Execute;
679+ SecondProcess.Execute;
680+
681+ while FirstProcess.Running or (FirstProcess.Output.NumBytesAvailable > 0) do
682+ begin
683+ if FirstProcess.Output.NumBytesAvailable > 0 then
684+ begin
685+ // make sure that we don't read more data than we have allocated
686+ // in the buffer
687+ ReadSize := FirstProcess.Output.NumBytesAvailable;
688+ if ReadSize > SizeOf(Buffer) then
689+ ReadSize := SizeOf(Buffer);
690+ // now read the output into the buffer
691+ ReadCount := FirstProcess.Output.Read(Buffer[0], ReadSize);
692+ // and write the buffer to the second process
693+ SecondProcess.Input.Write(Buffer[0], ReadCount);
694+
695+ // if SecondProcess writes much data to it's Output then
696+ // we should read that data here to prevent a deadlock
697+ // see the previous example "Reading Large Output"
698+ end;
699+ end;
700+ // Close the input on the SecondProcess
701+ // so it finishes processing it's data
702+ SecondProcess.CloseInput;
703+
704+ // and wait for it to complete
705+ // be carefull what command you run because it may not exit when
706+ // it's input is closed and the following line may loop forever
707+ while SecondProcess.Running do
708+ Sleep(1);
709+ // that's it! the rest of the program is just so the example
710+ // is a little 'useful'
711+
712+ // we will reuse Buffer to output the SecondProcess's
713+ // output to *this* programs stdout
714+ WriteLn('Grep output Start:');
715+ ReadSize := SecondProcess.Output.NumBytesAvailable;
702716 if ReadSize > SizeOf(Buffer) then
703717 ReadSize := SizeOf(Buffer);
704- // now read the output into the buffer
705- ReadCount := FirstProcess.Output.Read(Buffer[0], ReadSize);
706- // and write the buffer to the second process
707- SecondProcess.Input.Write(Buffer[0], ReadCount);
708-
709- // if SecondProcess writes much data to it's Output then
710- // we should read that data here to prevent a deadlock
711- // see the previous example "Reading Large Output"
712- end;
713- end;
714- // Close the input on the SecondProcess
715- // so it finishes processing it's data
716- SecondProcess.CloseInput;
717-
718- // and wait for it to complete
719- // be carefull what command you run because it may not exit when
720- // it's input is closed and the following line may loop forever
721- while SecondProcess.Running do
722- Sleep(1);
723- // that's it! the rest of the program is just so the example
724- // is a little 'useful'
725-
726- // we will reuse Buffer to output the SecondProcess's
727- // output to *this* programs stdout
728- WriteLn('Grep output Start:');
729- ReadSize := SecondProcess.Output.NumBytesAvailable;
730- if ReadSize > SizeOf(Buffer) then
731- ReadSize := SizeOf(Buffer);
732- if ReadSize > 0 then
733- begin
734- ReadCount := SecondProcess.Output.Read(Buffer, ReadSize);
735- WriteLn(Copy(Buffer,0, ReadCount));
736- end
737- else
738- WriteLn('grep did not find what we searched for. ', SecondProcess.ExitStatus);
739- WriteLn('Grep output Finish:');
740-
741- // free our process objects
742- FirstProcess.Free;
743- SecondProcess.Free;
744-end.
718+ if ReadSize > 0 then
719+ begin
720+ ReadCount := SecondProcess.Output.Read(Buffer, ReadSize);
721+ WriteLn(Copy(Buffer,0, ReadCount));
722+ end
723+ else
724+ WriteLn('grep did not find what we searched for. ', SecondProcess.ExitStatus);
725+ WriteLn('Grep output Finish:');
726+
727+ // free our process objects
728+ FirstProcess.Free;
729+ SecondProcess.Free;
730+ end.
745731```
746732
747-That's it. Now you can redirect output from one program to another.
733+That's it. Now you can redirect output from one program to another.
748734
749735#### Notes
750736
751-This example may seem overdone since it's possible to run "complicated" commands using a shell with TProcess like:
752-
753-``` pascal
737+This example may seem overdone since it's possible to run "complicated" commands using a shell with TProcess like:
738+```
754739Process.Commandline := 'sh -c "pwd | grep / -"';
755740```
756741
757-But our example is more crossplatform since it needs no modification to run on Windows or Linux etc. "sh" may or may not exist on your platform and is generally only available on *nix platforms. Also we have more flexibility in our example since you can read and write from/to the input, output and stderr of each process individually, which could be very advantageous for your project.
742+But our example is more crossplatform since it needs no modification to run on Windows or Linux etc. "sh" may or may not exist on your platform and is generally only available on *nix platforms. Also we have more flexibility in our example since you can read and write from/to the input, output and stderr of each process individually, which could be very advantageous for your project.
758743
759744### Redirecting input and output and running under root
760745
761-A 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.
762-
763-If 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.
746+A 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.
764747
765-A **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)
748+If 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.
766749
767-Large 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.
750+A **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>)
768751
769-``` pascal
752+Large 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.
753+```
770754program rootls;
771-
772-{ Demonstrates using TProcess, redirecting stdout/stderr to our stdout/stderr,
773-calling sudo on FreeBSD/Linux/macOS, and supplying input on stdin}
774-{$mode objfpc}{$H+}
775-
776-uses
777- Classes,
778- Math, {for min}
779- Process;
780-
781- procedure RunsLsRoot;
782- var
783- Proc: TProcess;
784- CharBuffer: array [0..511] of char;
785- ReadCount: integer;
786- ExitCode: integer;
787- SudoPassword: string;
788- begin
789- WriteLn('Please enter the sudo password:');
790- Readln(SudoPassword);
791- ExitCode := -1; //Start out with failure, let's see later if it works
792- Proc := TProcess.Create(nil); //Create a new process
793- try
794- Proc.Options := [poUsePipes, poStderrToOutPut]; //Use pipes to redirect program stdin,stdout,stderr
795- Proc.CommandLine := 'sudo -S ls /root'; //Run ls /root as root using sudo
796- // -S causes sudo to read the password from stdin.
797- Proc.Execute; //start it. sudo will now probably ask for a password
798-
799- // write the password to stdin of the sudo program:
800- SudoPassword := SudoPassword + LineEnding;
801- Proc.Input.Write(SudoPassword[1], Length(SudoPassword));
802- SudoPassword := '%*'; //short string, hope this will scramble memory a bit; note: using PChars is more fool-proof
803- SudoPassword := ''; // and make the program a bit safer from snooping?!?
804-
805- // main loop to read output from stdout and stderr of sudo
806- while Proc.Running or (Proc.Output.NumBytesAvailable > 0) or
807- (Proc.Stderr.NumBytesAvailable > 0) do
755+
756+ { Demonstrates using TProcess, redirecting stdout/stderr to our stdout/stderr,
757+ calling sudo on FreeBSD/Linux/macOS, and supplying input on stdin}
758+ {$mode objfpc}{$H+}
759+
760+ uses
761+ Classes,
762+ Math, {for min}
763+ Process;
764+
765+ procedure RunsLsRoot;
766+ var
767+ Proc: TProcess;
768+ CharBuffer: array [0..511] of char;
769+ ReadCount: integer;
770+ ExitCode: integer;
771+ SudoPassword: string;
808772 begin
809- // read stdout and write to our stdout
810- while Proc.Output.NumBytesAvailable > 0 do
811- begin
812- ReadCount := Min(512, Proc.Output.NumBytesAvailable); //Read up to buffer, not more
813- Proc.Output.Read(CharBuffer, ReadCount);
814- Write(StdOut, Copy(CharBuffer, 0, ReadCount));
815- end;
816- // read stderr and write to our stderr
817- while Proc.Stderr.NumBytesAvailable > 0 do
818- begin
819- ReadCount := Min(512, Proc.Stderr.NumBytesAvailable); //Read up to buffer, not more
820- Proc.Stderr.Read(CharBuffer, ReadCount);
821- Write(StdErr, Copy(CharBuffer, 0, ReadCount));
773+ WriteLn('Please enter the sudo password:');
774+ Readln(SudoPassword);
775+ ExitCode := -1; //Start out with failure, let's see later if it works
776+ Proc := TProcess.Create(nil); //Create a new process
777+ try
778+ Proc.Options := [poUsePipes, poStderrToOutPut]; //Use pipes to redirect program stdin,stdout,stderr
779+ Proc.CommandLine := 'sudo -S ls /root'; //Run ls /root as root using sudo
780+ // -S causes sudo to read the password from stdin.
781+ Proc.Execute; //start it. sudo will now probably ask for a password
782+
783+ // write the password to stdin of the sudo program:
784+ SudoPassword := SudoPassword + LineEnding;
785+ Proc.Input.Write(SudoPassword[1], Length(SudoPassword));
786+ SudoPassword := '%*'; //short string, hope this will scramble memory a bit; note: using PChars is more fool-proof
787+ SudoPassword := ''; // and make the program a bit safer from snooping?!?
788+
789+ // main loop to read output from stdout and stderr of sudo
790+ while Proc.Running or (Proc.Output.NumBytesAvailable > 0) or
791+ (Proc.Stderr.NumBytesAvailable > 0) do
792+ begin
793+ // read stdout and write to our stdout
794+ while Proc.Output.NumBytesAvailable > 0 do
795+ begin
796+ ReadCount := Min(512, Proc.Output.NumBytesAvailable); //Read up to buffer, not more
797+ Proc.Output.Read(CharBuffer, ReadCount);
798+ Write(StdOut, Copy(CharBuffer, 0, ReadCount));
799+ end;
800+ // read stderr and write to our stderr
801+ while Proc.Stderr.NumBytesAvailable > 0 do
802+ begin
803+ ReadCount := Min(512, Proc.Stderr.NumBytesAvailable); //Read up to buffer, not more
804+ Proc.Stderr.Read(CharBuffer, ReadCount);
805+ Write(StdErr, Copy(CharBuffer, 0, ReadCount));
806+ end;
807+ end;
808+ ExitCode := Proc.ExitStatus;
809+ finally
810+ Proc.Free;
811+ Halt(ExitCode);
822812 end;
823813 end;
824- ExitCode := Proc.ExitStatus;
825- finally
826- Proc.Free;
827- Halt(ExitCode);
828- end;
829- end;
830-
831-begin
832- RunsLsRoot;
833-end.
814+
815+ begin
816+ RunsLsRoot;
817+ end.
834818```
835819
836-Other 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.
820+Other 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.
837821
838822### Using fdisk with sudo on Linux
839823
840824The 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.**
841-
842-``` pascal
825+```
843826program getpartitioninfo;
844-{Originally contributed by Lazarus forums wjackon153. Please contact him for questions, remarks etc.
845-Modified from Lazarus snippet to FPC program for ease of understanding/conciseness by BigChimp}
846-
847-Uses
848- Classes, SysUtils, FileUtil, Process;
849-
850-var
851- hprocess: TProcess;
852- sPass: String;
853- OutputLines: TStringList;
854-
855-begin
856- sPass := 'yoursudopasswordhere'; // You need to change this to your own sudo password
857- OutputLines:=TStringList.Create; //... a try...finally block would be nice to make sure
858- // OutputLines is freed... Same for hProcess.
859-
860- // The following example will open fdisk in the background and give us partition information
861- // Since fdisk requires elevated priviledges we need to
862- // pass our password as a parameter to sudo using the -S
863- // option, so it will will wait till our program sends our password to the sudo application
864- hProcess := TProcess.Create(nil);
865- // On Linux/Unix/FreeBSD/macOS, we need specify full path to our executable:
866- hProcess.Executable := '/bin/sh';
867- // Now we add all the parameters on the command line:
868- hprocess.Parameters.Add('-c');
869- // Here we pipe the password to the sudo command which then executes fdisk -l:
870- hprocess.Parameters.add('echo ' + sPass + ' | sudo -S fdisk -l');
871- // Run asynchronously (wait for process to exit) and use pipes so we can read the output pipe
872- hProcess.Options := hProcess.Options + [poWaitOnExit, poUsePipes];
873- // Now run:
874- hProcess.Execute;
875-
876- // hProcess should have now run the external executable (because we use poWaitOnExit).
877- // Now you can process the process output (standard output and standard error), eg:
878- OutputLines.Add('stdout:');
879- OutputLines.LoadFromStream(hprocess.Output);
880- OutputLines.Add('stderr:');
881- OutputLines.LoadFromStream(hProcess.Stderr);
882- // Show output on screen:
883- writeln(OutputLines.Text);
884-
885- // Clean up to avoid memory leaks:
886- hProcess.Free;
887- OutputLines.Free;
888-
889- //Below are some examples as you see we can pass illegal characters just as if done from terminal
890- //Even though you have read elsewhere that you can not I assure with this method you can :)
891-
892- //hprocess.Parameters.Add('ping -c 1 www.google.com');
893- //hprocess.Parameters.Add('ifconfig wlan0 | grep ' + QuotedStr('inet addr:') + ' | cut -d: -f2');
894-
895- //Using QuotedStr() is not a requirement though it makes for cleaner code;
896- //you can use double quote and have the same effect.
897-
898- //hprocess.Parameters.Add('glxinfo | grep direct');
899-
900- // This method can also be used for installing applications from your repository:
901-
902- //hprocess.Parameters.add('echo ' + sPass + ' | sudo -S apt-get install -y pkg-name');
903-
904- end.
827+ {Originally contributed by Lazarus forums wjackon153. Please contact him for questions, remarks etc.
828+ Modified from Lazarus snippet to FPC program for ease of understanding/conciseness by BigChimp}
829+
830+ Uses
831+ Classes, SysUtils, FileUtil, Process;
832+
833+ var
834+ hprocess: TProcess;
835+ sPass: String;
836+ OutputLines: TStringList;
837+
838+ begin
839+ sPass := 'yoursudopasswordhere'; // You need to change this to your own sudo password
840+ OutputLines:=TStringList.Create; //... a try...finally block would be nice to make sure
841+ // OutputLines is freed... Same for hProcess.
842+
843+ // The following example will open fdisk in the background and give us partition information
844+ // Since fdisk requires elevated priviledges we need to
845+ // pass our password as a parameter to sudo using the -S
846+ // option, so it will will wait till our program sends our password to the sudo application
847+ hProcess := TProcess.Create(nil);
848+ // On Linux/Unix/FreeBSD/macOS, we need specify full path to our executable:
849+ hProcess.Executable := '/bin/sh';
850+ // Now we add all the parameters on the command line:
851+ hprocess.Parameters.Add('-c');
852+ // Here we pipe the password to the sudo command which then executes fdisk -l:
853+ hprocess.Parameters.add('echo ' + sPass + ' | sudo -S fdisk -l');
854+ // Run asynchronously (wait for process to exit) and use pipes so we can read the output pipe
855+ hProcess.Options := hProcess.Options + [poWaitOnExit, poUsePipes];
856+ // Now run:
857+ hProcess.Execute;
858+
859+ // hProcess should have now run the external executable (because we use poWaitOnExit).
860+ // Now you can process the process output (standard output and standard error), eg:
861+ OutputLines.Add('stdout:');
862+ OutputLines.LoadFromStream(hprocess.Output);
863+ OutputLines.Add('stderr:');
864+ OutputLines.LoadFromStream(hProcess.Stderr);
865+ // Show output on screen:
866+ writeln(OutputLines.Text);
867+
868+ // Clean up to avoid memory leaks:
869+ hProcess.Free;
870+ OutputLines.Free;
871+
872+ //Below are some examples as you see we can pass illegal characters just as if done from terminal
873+ //Even though you have read elsewhere that you can not I assure with this method you can :)
874+
875+ //hprocess.Parameters.Add('ping -c 1 www.google.com');
876+ //hprocess.Parameters.Add('ifconfig wlan0 | grep ' + QuotedStr('inet addr:') + ' | cut -d: -f2');
877+
878+ //Using QuotedStr() is not a requirement though it makes for cleaner code;
879+ //you can use double quote and have the same effect.
880+
881+ //hprocess.Parameters.Add('glxinfo | grep direct');
882+
883+ // This method can also be used for installing applications from your repository:
884+
885+ //hprocess.Parameters.add('echo ' + sPass + ' | sudo -S apt-get install -y pkg-name');
886+
887+ end.
905888```
906889
907890### Parameters which contain spaces (Replacing Shell Quotes)
908891
909-In the Linux shell it is possible to write quoted arguments like this:
910-
911-```text
892+In the Linux shell it is possible to write quoted arguments like this:
893+```
912894gdb --batch --eval-command="info symbol 0x0000DDDD" myprogram
913895```
914896
915-And GDB will receive 3 arguments (in addition to the first argument which is the full path to the executable):
916-
917-1. --batch
918-2. --eval-command=info symbol 0x0000DDDD
919-3. the full path to myprogram
897+And GDB will receive 3 arguments (in addition to the first argument which is the full path to the executable):
920898
921-The 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.
899+ 1. \--batch
900+ 2. \--eval-command=info symbol 0x0000DDDD
901+ 3. the full path to myprogram
922902
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');
903+The 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.
928904```
929-
930-And also remember to only pass full paths.
905+AProcess.Executable:='/usr/bin/gdb';
906+ AProcess.Parameters.Add('--batch');
907+ AProcess.Parameters.Add('--eval-command=info symbol 0x0000DDDD'); // note the absence of quoting here
908+ AProcess.Parameters.Add('/home/me/myprogram');
909+```
931910
932-TProcess.Commandline however does support some basic quoting for parameters with quoting. Quote the *whole* parameter containing spaces with double quotes. Like this:
911+And also remember to only pass full paths.
933912
934-``` pascal
935-AProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /home/me/myprogram';
913+TProcess.Commandline however does support some basic quoting for parameters with quoting. Quote the _whole_ parameter containing spaces with double quotes. Like this:
936914```
915+AProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /home/me/myprogram';
916+```
937917
938-The .CommandLine property is [deprecated](http://bugs.freepascal.org/view.php?id=14446) and bugreports to support more complicated quoting cases won't accepted.
918+
919+The .CommandLine property is [deprecated](<http://bugs.freepascal.org/view.php?id=14446>) and bugreports to support more complicated quoting cases won't accepted.
939920
940921## LCLIntf Alternatives
941922
942-Sometimes, 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.
923+Sometimes, 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.
943924
944925### Open document in default application
945926
946-In 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.
947-
948-``` pascal
927+In 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.
928+```
949929uses LCLIntf;
950-...
951-OpenDocument('manual.pdf');
952-...
930+ ...
931+ OpenDocument('manual.pdf');
932+ ...
953933```
954934
955-- [[opendocument|OpenDocument reference]]
935+ * [[opendocument|OpenDocument reference]]
956936
957937### Open web page in default web browser
958938
959-Just 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()
960-
961-``` pascal
939+Just 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()
940+```
962941uses LCLIntf;
963-...
964-OpenURL('www.lazarus.freepascal.org/');
942+ ...
943+ OpenURL('www.lazarus.freepascal.org/');
965944```
966945
967-See also:
968-
969-- [[OpenURL|OpenURL reference]]
946+See also:
970947
971-Or, you could use **TProcess** like this:
948+ * [[OpenURL|OpenURL reference]]
972949
973-``` pascal
950+Or, you could use **TProcess** like this:
951+```
974952uses Process;
975-
976-procedure OpenWebPage(URL: string);
977-// Apparently you need to pass your URL inside ", like "www.lazarus.freepascal.org"
978-var
979- Browser, Params: string;
980-begin
981- FindDefaultBrowser(Browser, Params);
982- with TProcess.Create(nil) do
983- try
984- Executable := Browser;
985- Params:=Format(Params, [URL]);
986- Params:=copy(Params,2,length(Params)-2); // remove "", the new version of TProcess.Parameters does that itself
987- Parameters.Add(Params);
988- Options := [poNoConsole];
989- Execute;
990- finally
991- Free;
992- end;
993-end;
953+
954+ procedure OpenWebPage(URL: string);
955+ // Apparently you need to pass your URL inside ", like "www.lazarus.freepascal.org"
956+ var
957+ Browser, Params: string;
958+ begin
959+ FindDefaultBrowser(Browser, Params);
960+ with TProcess.Create(nil) do
961+ try
962+ Executable := Browser;
963+ Params:=Format(Params, [URL]);
964+ Params:=copy(Params,2,length(Params)-2); // remove "", the new version of TProcess.Parameters does that itself
965+ Parameters.Add(Params);
966+ Options := [poNoConsole];
967+ Execute;
968+ finally
969+ Free;
970+ end;
971+ end;
994972```
995973
996974## WinExec
997975
998-The 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
976+The 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
999977
1000978## See also
1001979
1002-- [TProcess documentation](http://lazarus-ccr.sourceforge.net/docs/fcl/process/tprocess.html)
1003-- [[opendocument|OpenDocument]]
1004-- [[OpenURL]]
1005-- [[TProcessUTF8]]
1006-- [[TXMLPropStorage]]
1007-- [[Webbrowser]]
980+ * [TProcess documentation](<http://lazarus-ccr.sourceforge.net/docs/fcl/process/tprocess.html>)
981+ * [[opendocument|OpenDocument]]
982+ * [[OpenURL]]
983+ * [[TProcessUTF8]]
984+ * [[TXMLPropStorage]]
985+ * [[Webbrowser]]
1008986
1009987---
1010-*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).*
988+*From the Free Pascal wiki, page "Executing External Programs", as the Internet Archive kept it on 2026-01-08, last changed on 2025-08-20. 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