Sign in Join
Wiki copytemporary

Executing External Programs/ja: what changed

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

OldNewLine
11## 概要
22
33外部のプログラムを動かすにはいくつかの方法がありますが、このチュートリアルでは [[doc:fcl/process/tprocess.html|TProcessを]]中心に紹介しています。
44
55Delphiで ShellExecute や WinExec を使っていたなら、FPC/Lazarusにおける代替としてTProcessを使用するようにしましょう。(TProcess は cross-platformであり、Linuxでも有効です。)
66
77Note: FPC/Lazarus は ShellExecute 及び WinExec をサポートしていますが、win32のみで有効です。 あなたのプログラムが cross-platformを目標にしているなら、TProcessの使用が最善です! 次に、さまざまな手法の比較を表にします
88
9-<TABLE WIDTH="90%" CELLPADDING="1" CELLSPACING="1">
10-
11-<TR>
12-
13-<TD>
14-
15-<b>方法</b>
16-
17-</TD>
18-
19-<TD>
20-
21-<b>プラットフォーム</b>
22-
23-</TD>
24-
25-<TD>
26-
27-<b>一行で書けるか</b>
28-
29-</TD>
30-
31-<TD>
32-
33-<b>機能の程度</b>
34-
35-</TD>
9+方法
3610
37-</TR>
11+プラットフォーム
3812
39-<TR>
13+一行で書けるか
4014
41-<TD>
15+機能の程度
4216
4317SysUtils.ExecuteProcess
4418
45-</TD>
46-
47-<TD>
48-
4919クロスプラットフォーム
5020
51-</TD>
52-
53-<TD>
54-
5521Yes
5622
57-</TD>
58-
59-<TD>
60-
6123極めて限定的、同期的
6224
63-</TD>
64-
65-</TR>
66-
67-<TR>
68-
69-<TD>
70-
7125(ShellApi) ShellExecute
7226
73-</TD>
74-
75-<TD>
76-
7727MS Windows のみ
7828
79-</TD>
80-
81-<TD>
82-
8329Yes
8430
85-</TD>
86-
87-<TD>
88-
8931多数。より高位の/管理者権限で実行可能
9032
91-</TD>
92-
93-</TR>
94-
95-<TR>
96-
97-<TD>
98-
9933Unix fpsystem, fpexecve
10034
101-</TD>
102-
103-<TD>
104-
10535Unix のみ
10636
107-</TD>
108-
109-<TD>
110-
111-</TD>
112-
113-<TD>
114-
115-</TD>
116-
117-</TR>
118-
119-<TR>
120-
121-<TD>
122-
12337TProcess
12438
125-</TD>
126-
127-<TD>
128-
12939クロスプラットフォーム
13040
131-</TD>
132-
133-<TD>
134-
13541No
13642
137-</TD>
138-
139-<TD>
140-
14143全機能が可能
14244
143-</TD>
144-
145-</TR>
146-
147-<TR>
148-
149-<TD>
150-
15145RunCommand(InDir)
15246
153-</TD>
154-
155-<TD>
156-
15747クロスプラットフォーム **FPC 2.6.2以降が必要**
15848
159-</TD>
160-
161-<TD>
162-
16349Yes
16450
165-</TD>
166-
167-<TD>
168-
16951よくあるTProcess利用法をカバー
17052
171-</TD>
172-
173-</TR>
174-
175-<TR>
176-
177-<TD>
178-
17953(LCLIntf) OpenDocument
18054
181-</TD>
182-
183-<TD>
184-
18555クロスプラットフォーム
18656
187-</TD>
188-
189-<TD>
190-
19157Yes
19258
193-</TD>
194-
195-<TD>
196-
19759文書を開くだけ
198-
199-</TD>
200-
201-</TR>
202-
203-</TABLE>
20460
20561## SysUtils.ExecuteProcess
20662
20763(クロスプラットフォーム)\
20864最も簡単な方法は、SysUtils.ExecuteProcessを使用することです。ただし、モーダル(訳注: 外部プログラムに制御が移ること。下記)ですし、パイプもいかなるコントロールも使えません:
20965
21066``` pascal
21167SysUtils.ExecuteProcess(UTF8ToSys('実行ファイルへのフルパス'), '', [])
21268```
21369
21470呼出しは同期的です: 呼び出した側は、外部プログラムの動作が終了するまで「ハング」します - しかしアプリケーションの実行を進める前に、ユーザになにかをさせるには便利かもしれません。次の節の **TProcess** はもっと広い用途に用いることができ、おすすめです。Windows だけでいいなら、**ShellExecute** も使えます。
21571
21672- [ExecuteProcess reference](http://www.freepascal.org/docs-html/rtl/sysutils/executeprocess.html)
21773
21874## MS Windows : CreateProcess, ShellExecute と WinExec
21975
22076> **Note:** FPC/Lazarus は **CreateProcess**、**ShellExecute**、**WinExec** をサポートしてはいますが、プラットフォームは Win32/64 に限定されます。クロスプラットフォームなプログラムには **TProcess** をお使いください。
22177
22278> **Note:** WinExec は 16 bit 呼出しで、何年も前から Windows API の中で旧式化しています。最近の FPC は警告を発します。
22379
22480**ShellExecute** は MS Windows で標準的な関数です (ShellApi.h)。マイクロソフトによる解説は \[<http://msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85>).aspx documentation on MSDN\] をどうぞ。この関数が不安定だと思う方は、この解説の中の COM の初期化についての注釈をお読み下さい。
22581
22682``` pascal
22783uses ..., ShellApi;
22884
22985// 簡単な一行野郎(エラーを無視) :
23086if ShellExecute(0,nil, PChar('"C:\my dir\prog.exe"'),PChar('"C:\somepath\some_doc.ext"'),nil,1) =0 then;
23187
23288// バッチファイルを実行 :
23389if ShellExecute(0,nil, PChar('cmd'),PChar('/c mybatch.bat'),nil,1) =0 then;
23490
23591// 任意のフォルダでコマンドプロンプトを開く :
23692if ShellExecute(0,nil, PChar('cmd'),PChar('/k cd \path'),nil,1) =0 then;
23793
23894// (隠れたコマンドウィンドウを通して)start コマンドを使ってデフォルトブラウザでウェブページの URL を開く:
23995if ShellExecute(0,nil, PChar('cmd'),PChar('/c start www.lazarus.freepascal.org/'),nil,0) =0 then;
24096
24197// あるいは便利な手続:
24298procedure RunShellExecute(const prog,params:string);
24399begin
244100 // ( ハンドル, nil/'open'/'edit'/'find'/'explore'/'print', // 'open' は必ずしも必要ではない
245101 // パス付きプログラム名, 引数, 作業用フォルダ,
246102 // 0=隠し / 1=SW_SHOWNORMAL / 3=最大化 / 7=最小化) //定数 SW_ は Windows ユニットにある
247103 if ShellExecute(0,'open',PChar(prog),PChar(params),PChar(extractfilepath(prog)),1) >32 then; //成功
248104 // 0..32 はエラー
249105end;
250106```
251107
252108WideChar 版の ShellExecuteExW や AnsiChar 版の ShellExecuteExA もあります。
253109
254110The fMask option can also use SEE_MASK_DOENVSUBST or SEE_MASK_FLAG_NO_UI or SEE_MASK_NOCLOSEPROCESS, etc.
255111
256112If 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).
257113
258114### Using ShellExecuteEx for elevation/administrator permissions
259115
260116If you need to execute external program with administrator/elevated privileges, you can use the **runas** method with the alternative ShellExecuteEx function:
261117
262118``` pascal
263119uses ShellApi, ...;
264120
265121function RunAsAdmin(const Handle: Hwnd; const Path, Params: string): Boolean;
266122var
267123 sei: TShellExecuteInfoA;
268124begin
269125 FillChar(sei, SizeOf(sei), 0);
270126 sei.cbSize := SizeOf(sei);
271127 sei.Wnd := Handle;
272128 sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
273129 sei.lpVerb := 'runas';
274130 sei.lpFile := PAnsiChar(Path);
275131 sei.lpParameters := PAnsiChar(Params);
276132 sei.nShow := SW_SHOWNORMAL;
277133 Result := ShellExecuteExA(@sei);
278134end;
279135
280136procedure TFormMain.RunAddOrRemoveApplication;
281137begin
282138 // Example that uses elevated rundll to open the Control Panel to Programs and features
283139 RunAsAdmin(FormMain.Handle, 'rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl', '');
284140end;
285141```
286142
287143## Unix fpsystem, fpexecve と shell
288144
289145これらの関数はプラットフォーム依存です。
290146
291147- [fpsystem reference](http://www.freepascal.org/docs-html/rtl/unix/fpsystem.html)
292148- [fpexecve reference](http://www.freepascal.org/docs-html/rtl/baseunix/fpexecve.html)
293149- [shell reference](http://www.freepascal.org/docs-html/rtl/unix/shell.html)
294150
2951511.0.x では **Unix.Shell** は非推奨になっています。fpsystem をお使いください。
296152
297153## TProcess
298154
299155TProcessを使用して外部プログラムを実行することができます。TProcessを使うメリットは次の通りです。
300156
301157- プラットホームに依存しない
302158- 標準入力からの読み込み、標準出力への書き込みが可能
303159- コマンドの終了を待つことも、待たずに実行を続けることもできる
304160
305161重要な注意点:
306162
307163- TProcess はターミナルでもシェルでもありません。直接スクリプトを実行することや、 "\|"、"\>"、"\<"、"&" などを使って出力結果をリダイレクトすることはできません。 しかし、 TProcess を用いて同様な結果を得ることができます。いくつかのサンプルを下に示します。
308164- おそらく Linux/Unix では、実行ファイルのフルパスを指定する必要があります。例えば 'cp' ではなく '/bin/cp' を使います。実行ファイルの存在するディレクトリが標準的な PATH 環境変数に含まれるならば、LCL の [[doc:lcl/fileutil/index.html|FileUtil]] にある [[doc:lcl/fileutil/finddefaultexecutablepath.html|FindDefaultExecutablePath]] 関数が使えます。
309165- Windows では、コマンドに PATH が通っていれば、フルパスを指定する必要はありません。
310166- [[doc:fcl/process/tprocess.html|TProcess reference]]
311167
312168### 単純な見本
313169
314170``` pascal
315171 // これはどのようにして外部プログラムを実行するかを示すデモプログラムです
316172 program launchprogram;
317173
318174 // 関数や手続きを使用するためにファイルをインクルードします
319175 uses
320176 Classes, SysUtils, Process;
321177
322178 // "TProcess"型の変数"AProcess"を定義します
323179 var
324180 AProcess: TProcess;
325181
326182 // ここからプログラムが開始します
327183 begin
328184 // TProcessオブジェクトを生成し、変数AProcessにアサインします
329185 AProcess := TProcess.Create(nil);
330186
331187 // AProcessに実行するコマンドを伝えます
332188 // FreePascalコンパイラを実行してみましょう
333189 AProcess.Executable:= 'ppc386';
334190 // ppc386に渡すコマンドライン引数を与えます (というわけで ppc386 -h を実行することになります):
335191 AProcess.Parameters.Add('-h');
336192 // .Executable と .Parameters は以前の版の FPC にはなく、
337193 // .CommandLine プロパティを用いていました。これはまだ残っていますが、旧式です。
338194 // 使わないでください。
339195
340196
341197 // プログラムを走らせるときの、オプションを定義しましょう
342198 // このオプションは、実行した外部プログラムが停止するまで、
343199 // このプログラムが動かないようにします vvvvvvvvvvvvvv
344200 AProcess.Options := AProcess.Options + [poWaitOnExit];
345201
346202 // AProcessにコマンドを実行させましょう
347203 AProcess.Execute;
348204
349205 // ppc386が停止するまで、これは実行されません。
350206 AProcess.Free;
351207 end.
352208```
353209
354210おめでとう!自分のプログラムから外部プログラムを実行する方法を学べました。
355211
356212### より向上した(でもまだ正しくない)見本
357213
358214それでは、どのようにして実行した外部プログラムの出力を読めばよいでしょう? 先ほどの例を拡張してみましょう。
359215
360216**この例は学びやすいように簡単に書かれています。しかし、最終製品のコードをこう書いてはいけません。かわりに #巨大な出力を読み取る のコードを使います。**
361217
362218``` pascal
363219 // これはどのようにして外部プログラムを実行させ、その出力を読むかを
364220 // 示す(問題のある)デモプログラムです。
365221 program launchprogram;
366222
367223 // 関数や手続きを使用するためにファイルをインクルードします
368224 uses
369225 Classes, SysUtils, Process;
370226
371227 // "TProcess"型の変数"AProcess"を定義します
372228 // また、プログラムの出力からデータを読み取り、
373229 // 保存する TStringList を追加します。
374230 var
375231 AProcess: TProcess;
376232 AStringList: TStringList;
377233
378234 // ここからプログラムが開始します
379235 begin
380236 // TProcessオブジェクトを生成し、変数AProcessにアサインします
381237 AProcess := TProcess.Create(nil);
382238
383239 // TStringListオブジェクトを生成します
384240 AStringList := TStringList.Create;
385241
386242 // AProcessに実行するコマンドを伝えます
387243 // FreePascalコンパイラを実行してみましょう
388244 AProcess.CommandLine := 'ppc386 -h';
389245
390246 // プログラムを走らせるときの、オプションを定義しましょう
391247 // [poWaitOnExit]は、実行した外部プログラムが停止するまで、
392248 // このプログラムが動かないようにします
393249 // また、[poUsePipes]でファイルの出力を読みたいことを伝えます
394250 AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
395251
396252 // AProcessは実行するコマンドラインを知っています
397253 AProcess.Execute;
398254
399255 // ppc386が停止するまで、次にいきません
400256
401257 // プログラムの出力を、TStringListに読み込みます。
402258 AStringList.LoadFromStream(AProcess.Output);
403259
404260 // ファイルに保存します
405261 AStringList.SaveToFile('output.txt');
406262
407263 // ファイルが保存されたので、
408264 // TStringListとTProcessを開放します
409265 AStringList.Free;
410266 AProcess.Free;
411267 end.
412268```
413269
414270### 巨大な出力を読み取る
415271
416272前述したサンプルでは、外部プログラムの終了まで待ち、その後に外部プログラムの出力内容を読み取りました。
417273
418274ここで、外部プログラムが大量のデータを出力すると想定した場合、パイプが満杯になるとパイプの内容が読み出されるまで外部プログラムはストップします。しかし、外部プログラムが終わるまでは、読み出し手のプログラムはパイプの内容を読み出せないため、袋小路に陥ります。
419275
420276そこで、次のサンプルでは poWaitOnExitを使わず、プログラムの実行中に出力から読むようにしました。出力はメモリストリームに保存され、追って TStringList に読み出されます。
421277
422278``` pascal
423279program procoutlarge;
424280{
425281 Copyright (c) 2004-2011 by Marc Weustink and contributors
426282
427283 This example is created in the hope that it will be useful,
428284 but WITHOUT ANY WARRANTY; without even the implied warranty of
429285 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
430286}
431287// これはどのようにして外部プログラムを実行させ、その出力を読むかを
432288// 示す(実用的な)デモプログラムです。
433289// Memorystream が一時的な出力バッファとして使われます
434290uses
435291 Classes, Process, SysUtils;
436292
437293const
438294 READ_BYTES = 2048;
439295
440296var
441297 OurCommand: String;
442298 OutputLines: TStringList;
443299 MemStream: TMemoryStream;
444300 OurProcess: TProcess;
445301 NumBytes: LongInt;
446302 BytesRead: LongInt;
447303
448304begin
449305 // 一時的な Memorystream が出力バッファとして使われます
450306 MemStream := TMemoryStream.Create;
451307 BytesRead := 0;
452308
453309 OurProcess := TProcess.Create(nil);
454310 // ディレクトリの再帰的読み出しは好例です。
455311 OurCommand:='invalid command, please fix the IFDEFS.';
456312 {$IFDEF Windows}
457313 //dir は CMD.EXE の内部コマンドなので直接使えません。
458314 //そこでシェルを使います:
459315 OurCommand:='cmd.exe /c "dir /s c:\windows\"';
460316 {$ENDIF Windows}
461317 {$IFDEF Unix}
462318 //LinuxやUnixてテストしてください
463319 OurCommand := 'ls --recursive --all -l /';
464320 {$ENDIF Unix}
465321 writeln('-- Going to run: ' + OurCommand);
466322 OurProcess.CommandLine := OurCommand;
467323
468324 // 出力サイズが不明であり、poWaintOnExit は使用できません。
469325 // Linux では出力パイプの大きさは2KBであり、
470326 // データの大きさがこれ以上である場合、デッドロックに乗り上げる
471327 // ことになります。
472328 OurProcess.Options := [poUsePipes];
473329 WriteLn('-- External program run started');
474330 OurProcess.Execute;
475331 while True do
476332 begin
477333 // メモリ空間を確保します
478334 MemStream.SetSize(BytesRead + READ_BYTES);
479335
480336 // 読んでいきます
481337 NumBytes := OurProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES);
482338 if NumBytes > 0 // All read() calls will block, except the final one.
483339 then begin
484340 Inc(BytesRead, NumBytes);
485341 Write('.') //スクリーンに進行状態を表示します
486342 end else
487343 BREAK // プログラムの実行が終了します
488344 end;
489345 if BytesRead > 0 then WriteLn;
490346 MemStream.SetSize(BytesRead);
491347 WriteLn('-- External program run complete');
492348
493349 OutputLines := TStringList.Create;
494350 OutputLines.LoadFromStream(MemStream);
495351 WriteLn('-- External program output line count = ', OutputLines.Count, ' --');
496352 for NumBytes := 0 to OutputLines.Count - 1 do
497353 begin
498354 WriteLn(OutputLines[NumBytes]);
499355 end;
500356 WriteLn('-- Program end');
501357 OutputLines.Free;
502358 OurProcess.Free;
503359 MemStream.Free;
504360end.
505361```
506362
507363### TProcessの出力と入力を使用する
508364
509365[Lazarus-CCR SVN](https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/examples/process)のprocessdemoを参照してください。
510366
511367### TProcess利用のヒント
512368
513369クロスプラットフォームのアプリケーションを作成する場合、"{\$IFDEF}s" と "{\$ENDIF}s" 命令を利用することで、OSに応じてコマンドラインを変更できます。
514370
515371例:
516372
517373``` pascal
518374 {...}
519375 AProcess:TProcess.Create(nil)
520376 {$IFDEF WIN32}
521377 AProcess.CommandLine := 'calc.exe'; //Windows Calc
522378 {$ENDIF}
523379 {$IFDEF LINUX}
524380 AProcess.CommandLine := 'kcalc'; //KDE Calc
525381 {$ENDIF}
526382 AProcess.Execute; //右の書き方もできます。 AProcess.Active:=True
527383 {...}
528384```
529385
530386### OS X のアプリケーションバンドルを前面に出す
531387
532388**アプリケーションバンドル** は、バンドル内の実行ファイルを指定することで TProcess から起動することができます。たとえば:
533389
534390`AProcess.Executable:='/Applications/iCal.app/Contents/MacOS/iCal';`
535391
536392これで、「カレンダー」が起動します。しかし、ウインドゥは現在使用中のアプリケーションの後ろ側にいってしまいます。アプリケーションを前面に出すには、**open** ユーティリティを引数 **-n** 付きで用います。
537393
538394`AProcess.Executable:='/usr/bin/open';`\
539395`AProcess.Parameters.Add('-n');`\
540396`AProcess.Parameters.Add('/Application/iCal.app');`
541397
542398アプリケーションが引数を要求する場合、**open** に、**--arg** 引数を渡すことができます。アプリケーションに渡す引数はその後に書きます:
543399
544400`AProcess.Parameters.Add('--args');`\
545401`AProcess.Parameters.Add('argument1');`\
546402`AProcess.Parameters.Add('argument2');`
547403
548404### aspell processによるプロセス間通信(talking)のサンプル
549405
550406[pasdoc](http://pasdoc.sourceforge.net/)のソースコードを見ると、パイプを介して実行中のaspellプロセスと通信することでスペルチェックを行う二つのユニットがあります:
551407
552408- [PasDoc_ProcessLineTalk.pas unit](http://pasdoc.svn.sourceforge.net/viewvc/*checkout*/pasdoc/trunk/source/component/PasDoc_ProcessLineTalk.pas) は、TProcessを継承するTProcessLineTalkクラスを実装しています。 このクラスによって、どんなプロセスとの間でも行単位の通信が容易にできます。
553409
554410- [PasDoc_Aspell.pas units](http://pasdoc.svn.sourceforge.net/viewvc/*checkout*/pasdoc/trunk/source/component/PasDoc_Aspell.pas) は、TAspellProcessクラスを実装しています。 このクラスは、基礎となるTProcessLineTalkのインスタンスをaspellプロセスの実行と実行中のaspellプロセスとの通信に用いながら、スペルチェックを行います。
555411
556412両ユニットは pasdoc の残りのソースから比較的独立しています。 パイプを介して他のプログラムを実行し、プロセスと通信するためにTPorcess を用いる実例として役に立つでしょう。
557413
558414### "\| \< \>" みたいなシェルの演算子を置き換える
559415
560416もっと複雑なコマンド、つまり、パイプやリダイレクトによって他のコマンドやファイルにデータを流し込むコマンドを実行させたい場合があります。例えば、
561417
562418``` pascal
563419ShellExecute('firstcommand.exe | secondcommand.exe');
564420```
565421
566422あるいは
567423
568424``` pascal
569425ShellExecute('dir > output.txt');
570426```
571427
572428TProcess でこれを実行しても上手く行きません。つまり、
573429
574430``` pascal
575431// 動作しない
576432Process.CommandLine := 'firstcommand.exe | secondcommand.exe';
577433Process.Execute;
578434```
579435
580436#### リダイレクトのための特別な演算子が働かない理由
581437
582438TProcess はシェル環境ではなくて、たんなる一つのプロセスです。二つではなく、たった一つの。しかし、期待通りに出力をリダイレクトすることができます。次の #TProcess で出力をリダイレクトする方法 をご覧下さい。
583439
584440### TProcess で出力をリダイレクトする方法 
585441
586442TProcess のインスタンスを、コマンド**毎に**用意すれば、コマンドの出力を別のコマンドに送ることができます。
587443
588444ここではあるプロセスの出力を他のプロセスにリダイレクトする方法を示します。あるプロセスの出力をファイルにリダイレクトする方法は、#巨大な出力を読み取るの例をご覧下さい。
589445
590446poStderrToOutPut オプションを指定すれば、標準出力(stdout)だけでなく、標準エラー出力(stderr)のリダイレクトも可能です。この例で、二つ目のプロセスがこのオプションを使っています。
591447
592448``` pascal
593449program Project1;
594450
595451uses
596452 Classes, sysutils, process;
597453
598454var
599455 FirstProcess,
600456 SecondProcess: TProcess;
601457 Buffer: array[0..127] of char;
602458 ReadCount: Integer;
603459 ReadSize: Integer;
604460begin
605461 FirstProcess := TProcess.Create(nil);
606462 SecondProcess := TProcess.Create(nil);
607463
608464 FirstProcess.Options := [poUsePipes];
609465 SecondProcess.Options := [poUsePipes,poStderrToOutPut];
610466
611467 FirstProcess.CommandLine := 'pwd';
612468 SecondProcess.CommandLine := 'grep '+ DirectorySeparator+ ' -';
613469 // これで "pwd | grep / -" と同等になるはずです
614470
615471 FirstProcess.Execute;
616472 SecondProcess.Execute;
617473
618474 while FirstProcess.Running or (FirstProcess.Output.NumBytesAvailable > 0) do
619475 begin
620476 if FirstProcess.Output.NumBytesAvailable > 0 then
621477 begin
622478 // 読み込むデータ量がバッファの大きさを超えないようにする
623479 ReadSize := FirstProcess.Output.NumBytesAvailable;
624480 if ReadSize > SizeOf(Buffer) then
625481 ReadSize := SizeOf(Buffer);
626482 // 出力をバッファに読み込む
627483 ReadCount := FirstProcess.Output.Read(Buffer[0], ReadSize);
628484 // バッファの中身を第二のプロセスに書き込む
629485 SecondProcess.Input.Write(Buffer[0], ReadCount);
630486
631487 // 第二のプロセスが出力するデータが多過ぎるとデッドロックを起こします。
632488 // その前にデータを読まなければなりません。
633489 // 「巨大な出力を読み取る」の例をご覧下さい。
634490 end;
635491 end;
636492 // 第二のプロセスの入力を閉じます。
637493 // これで第二のプロセスの処理が終わります。
638494 SecondProcess.CloseInput;
639495
640496 // 終了を待ちます。
641497 // どんなコマンドを実行するか良く注意すべきです。
642498 // 入力を閉じても終了しないコマンドだと、
643499 // 次の行が無限ループに突入します。
644500 while SecondProcess.Running do
645501 Sleep(1);
646502 // こんな感じ。ここから先も少し役立ちますよ。
647503
648504 // バッファを第二プロセスの出力用に再利用します。
649505 // 出力はこのプログラムの標準出力に送られます。
650506 WriteLn('Grep output Start:');
651507 ReadSize := SecondProcess.Output.NumBytesAvailable;
652508 if ReadSize > SizeOf(Buffer) then
653509 ReadSize := SizeOf(Buffer);
654510 if ReadSize > 0 then
655511 begin
656512 ReadCount := SecondProcess.Output.Read(Buffer, ReadSize);
657513 WriteLn(Copy(Buffer,0, ReadCount));
658514 end
659515 else
660516 WriteLn('grep did not find what we searched for. ', SecondProcess.ExitStatus);
661517 WriteLn('Grep output Finish:');
662518
663519 // プロセスオブジェクトを解放します
664520 FirstProcess.Free;
665521 SecondProcess.Free;
666522end.
667523```
668524
669525はい、これであるプログラムの出力を別のプログラムやファイルに流し込むことができました。
670526
671527#### 注
672528
673529この例は、「やりすぎ」に見えるかもしれません。というのも、TProcess からシェルを起動してやれば「複雑な」コマンドも実行できるからです。こんなふうに:
674530
675531``` pascal
676532Process.Commandline := 'sh -c "pwd | grep / -"';
677533```
678534
679535しかし、上記の例はもっとクロスプラットフォームです。Windows でも Linux でもそのまま走ります。"sh" コマンドがあろうがなかろうが構いません(UNIX系のシステムにしかないのが普通です)。また、この例はもっと柔軟です。各プロセス独立にそれぞれの標準入力、標準出力、標準エラー出力を読み書きすることができるからです。プロジェクトの中で、これが利点となることがあるでしょう。
680536
681537### 入出力のリダイレクトと root 権限での実行
682538
683539UNIXやOS Xでいつも問題になるのが、プログラムを root 権限で(あるいはもっと一般的に、他のユーザアカウントで)実行したい場合です。*ping* コマンドを走らせるのが実例になるでしょう。
684540
685541If 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.
686542
687543A **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)
688544
689545Large 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.
690546
691547``` pascal
692548program rootls;
693549
694550{ Demonstrates using TProcess, redirecting stdout/stderr to our stdout/stderr,
695551calling sudo on Linux/macOS, and supplying input on stdin}
696552{$mode objfpc}{$H+}
697553
698554uses
699555 Classes,
700556 Math, {for min}
701557 Process;
702558
703559 procedure RunsLsRoot;
704560 var
705561 Proc: TProcess;
706562 CharBuffer: array [0..511] of char;
707563 ReadCount: integer;
708564 ExitCode: integer;
709565 SudoPassword: string;
710566 begin
711567 WriteLn('Please enter the sudo password:');
712568 Readln(SudoPassword);
713569 ExitCode := -1; //Start out with failure, let's see later if it works
714570 Proc := TProcess.Create(nil); //Create a new process
715571 try
716572 Proc.Options := [poUsePipes, poStderrToOutPut]; //Use pipes to redirect program stdin,stdout,stderr
717573 Proc.CommandLine := 'sudo -S ls /root'; //Run ls /root as root using sudo
718574 // -S causes sudo to read the password from stdin.
719575 Proc.Execute; //start it. sudo will now probably ask for a password
720576
721577 // write the password to stdin of the sudo program:
722578 SudoPassword := SudoPassword + LineEnding;
723579 Proc.Input.Write(SudoPassword[1], Length(SudoPassword));
724580 SudoPassword := '%*'; //short string, hope this will scramble memory a bit; note: using PChars is more fool-proof
725581 SudoPassword := ''; // and make the program a bit safer from snooping?!?
726582
727583 // main loop to read output from stdout and stderr of sudo
728584 while Proc.Running or (Proc.Output.NumBytesAvailable > 0) or
729585 (Proc.Stderr.NumBytesAvailable > 0) do
730586 begin
731587 // read stdout and write to our stdout
732588 while Proc.Output.NumBytesAvailable > 0 do
733589 begin
734590 ReadCount := Min(512, Proc.Output.NumBytesAvailable); //Read up to buffer, not more
735591 Proc.Output.Read(CharBuffer, ReadCount);
736592 Write(StdOut, Copy(CharBuffer, 0, ReadCount));
737593 end;
738594 // read stderr and write to our stderr
739595 while Proc.Stderr.NumBytesAvailable > 0 do
740596 begin
741597 ReadCount := Min(512, Proc.Stderr.NumBytesAvailable); //Read up to buffer, not more
742598 Proc.Stderr.Read(CharBuffer, ReadCount);
743599 Write(StdErr, Copy(CharBuffer, 0, ReadCount));
744600 end;
745601 end;
746602 ExitCode := Proc.ExitStatus;
747603 finally
748604 Proc.Free;
749605 Halt(ExitCode);
750606 end;
751607 end;
752608
753609begin
754610 RunsLsRoot;
755611end.
756612```
757613
758614Other 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.
759615
760616### Using fdisk with sudo on Linux
761617
762618The following example shows how to run fdisk on a Linux machine using the sudo command to get root permissions.
763619
764620``` pascal
765621program getpartitioninfo;
766622{Originally contributed by Lazarus forums wjackon153. Please contact him for questions, remarks etc.
767623Modified from Lazarus snippet to FPC program for ease of understanding/conciseness by BigChimp}
768624
769625Uses
770626 Classes, SysUtils, FileUtil, Process;
771627
772628var
773629 hprocess: TProcess;
774630 sPass: String;
775631 OutputLines: TStringList;
776632
777633begin
778634 sPass := 'yoursudopasswordhere'; // You need to change this to your own sudo password
779635 OutputLines:=TStringList.Create; //... a try...finally block would be nice to make sure
780636 // OutputLines is freed... Same for hProcess.
781637
782638 // The following example will open fdisk in the background and give us partition information
783639 // Since fdisk requires elevated priviledges we need to
784640 // pass our password as a parameter to sudo using the -S
785641 // option, so it will will wait till our program sends our password to the sudo application
786642 hProcess := TProcess.Create(nil);
787643 // On Linux/Unix/macOS, we need specify full path to our executable:
788644 hProcess.Executable := '/bin/sh';
789645 // Now we add all the parameters on the command line:
790646 hprocess.Parameters.Add('-c');
791647 // Here we pipe the password to the sudo command which then executes fdisk -l:
792648 hprocess.Parameters.add('echo ' + sPass + ' | sudo -S fdisk -l');
793649 // Run asynchronously (wait for process to exit) and use pipes so we can read the output pipe
794650 hProcess.Options := hProcess.Options + [poWaitOnExit, poUsePipes];
795651 // Now run:
796652 hProcess.Execute;
797653
798654 // hProcess should have now run the external executable (because we use poWaitOnExit).
799655 // Now you can process the process output (standard output and standard error), eg:
800656 OutputLines.Add('stdout:');
801657 OutputLines.LoadFromStream(hprocess.Output);
802658 OutputLines.Add('stderr:');
803659 OutputLines.LoadFromStream(hProcess.Stderr);
804660 // Show output on screen:
805661 writeln(OutputLines.Text);
806662
807663 // Clean up to avoid memory leaks:
808664 hProcess.Free;
809665 OutputLines.Free;
810666
811667 //Below are some examples as you see we can pass illegal characters just as if done from terminal
812668 //Even though you have read elsewhere that you can not I assure with this method you can :)
813669
814670 //hprocess.Parameters.Add('ping -c 1 www.google.com');
815671 //hprocess.Parameters.Add('ifconfig wlan0 | grep ' + QuotedStr('inet addr:') + ' | cut -d: -f2');
816672
817673 //Using QuotedStr() is not a requirement though it makes for cleaner code;
818674 //you can use double quote and have the same effect.
819675
820676 //hprocess.Parameters.Add('glxinfo | grep direct');
821677
822678 // This method can also be used for installing applications from your repository:
823679
824680 //hprocess.Parameters.add('echo ' + sPass + ' | sudo -S apt-get install -y pkg-name');
825681
826682 end.
827683```
828684
829685### 空白を含む引数 (シェルの引用符を置き換える)
830686
831687Linuxのシェルでは、引数を引用符でくるむことができます。たとえばこのように:
832688
833689`gdb --batch --eval-command="info symbol 0x0000DDDD" myprogram`
834690
835691このとき、GDB が受け取る引数の数は 3 です(その他に、第0の引数として、実行ファイルのフルパスが渡されます):
836692
8376931. --batch
8386942. --eval-command=info symbol 0x0000DDDD
8396953. the full path to myprogram
840696
841697TProcess は空白を含む引数を渡せますが、引数の一部を囲むのではなく、全体を囲みます。このように:
842698
843699`TProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /home/me/myprogram';`
844700
845701フルパスしか渡せないことを思い出してください。
846702
847703これに関連して、次の議論をお読みください: <http://bugs.freepascal.org/view.php?id=14446>
848704
849705## (Process.)RunCommand
850706
851707FPC 2.6.2 では、Process ユニットに、TProcess のヘルパ関数が加えられました。これは [[Projects using Lazarus|fpcup]] プロジェクトで用いられた TProcess のラッパをもとにしています。 これらの関数は、基礎から中級クラスの用法を想定しており、一行から、大量の出力行までサポートしています。
852708
853709以下、簡単な用例です
854710
855711``` pascal
856712uses Process;
857713...
858714var s : ansistring;
859715...
860716if RunCommand('/bin/bash',['-c','echo $PATH'],s) then
861717 writeln(s);
862718```
863719
864720オーヴァロードした新たな RunCommand はプログラムの終了コードを返します。RunCommandInDir は、コマンドを p.CurrentDirectory で決めた他のディレクトリで実行します:
865721
866722``` pascal
867723function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string; var exitstatus:integer): integer;
868724function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string): boolean;
869725function RunCommand(const exename:string;const commands:array of string;var outputstring:string): boolean;
870726```
871727
872728## LCLIntf による別法
873729
874730特定の外部プログラムを明示的に呼ぶ必要がなく、ある文書を開くためになんらかのアプリケーションが動けばいいという場合があります。どのアプリケーションでその文書を開くかは OS まかせで、文書のタイプに合わせたデフォルトのアプリケーションが用いられます。次にいくつか例を挙げます。
875731
876732### デフォルトのアプリケーションで文書を開く
877733
878734特定のプログラムを実行したいというより、ある文書/ファイルを、関連づけられたデフォルトのアプリケーションで開きたい場合があります。 これは OS に依存します。Lazarus ではこのような場合のためにプラットフォームに依存しない手続き、**OpenDocument** を用意しています。呼出し側はブロックされずそのまま実行を続けます。
879735
880736``` pascal
881737uses LCLIntf;
882738...
883739OpenDocument('manual.pdf');
884740...
885741```
886742
887743- [[opendocument|OpenDocument reference]]
888744
889745### デフォルトのWebブラウザでWebページを開く
890746
891747OpenURL に開きたい URL を渡すだけです。場合によっては、最初の <http://> は不要です。ファイル名を渡すと OpenDocument() と同じ結果が得られます。
892748
893749``` pascal
894750uses LCLIntf;
895751...
896752OpenURL('www.lazarus.freepascal.org/');
897753```
898754
899755関連項目:
900756
901757- [[OpenURL|OpenURL reference]]
902758
903759**TProcess** をこんなふうに使ってもいけます:
904760
905761``` pascal
906762uses Process;
907763
908764procedure OpenWebPage(URL: string);
909765// URL は "" でかこってください。例えば、"www.lazarus.freepascal.org"
910766var
911767 Browser, Params: string;
912768begin
913769 FindDefaultBrowser(Browser, Params);
914770 with TProcess.Create(nil) do
915771 try
916772 Executable := Browser;
917773 Params:=Format(Params, [URL]);
918774 Params:=copy(Params,2,length(Params)-2); // remove "", the new version of TProcess.Parameters does that itself
919775 Parameters.Add(Params);
920776 Options := [poNoConsole];
921777 Execute;
922778 finally
923779 Free;
924780 end;
925781end;
926782```
927783
928784## 関連項目
929785
930786- [TProcess documentation](http://lazarus-ccr.sourceforge.net/docs/fcl/process/tprocess.html)
931787- [[opendocument]]
932788- [[OpenURL]]
933789- [[TXMLPropStorage]]
934790- [[Webbrowser]]
935791
936792---
937793*From the Free Pascal wiki, page "Executing External Programs/ja", as it stood on 2021-04-01 after a change by Trev, 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