Difference between revisions of "Executing External Programs/ja"
Miyatakejiro (talk | contribs) |
Miyatakejiro (talk | contribs) |
||
Line 10: | Line 10: | ||
Note: FPC/Lazarus は ShellExecute 及び WinExec をサポートしていますが、win32のみで有効です。 あなたのプログラムが cross-platformを目標にしているなら、TProcessの使用が最善です! | Note: FPC/Lazarus は ShellExecute 及び WinExec をサポートしていますが、win32のみで有効です。 あなたのプログラムが cross-platformを目標にしているなら、TProcessの使用が最善です! | ||
+ | |||
+ | == SysUtils.ExecuteProcess == | ||
+ | |||
+ | パイプや他のコントロールから成るフォームを使用しない場合、最も簡単な方法は、 SysUtils.ExecuteProcess('/full/path/to/binary',['arg1','arg2'])を使用することです。 | ||
==TProcess== | ==TProcess== | ||
− | + | あなたはTProcessを使って外部プログラムを実行することができます。TProcessを使うメリットは次の通りです。 | |
− | * | + | *プラットホームに依存しない |
*stdinからの読み込み、stdoutへの書き込みが可能 | *stdinからの読み込み、stdoutへの書き込みが可能 | ||
+ | |||
+ | |||
+ | Note: TProcess は terminal/shell ではないので、直接スクリプトを実行することや、出力結果をリダイレクトすることは出来ません。 しかし、 TProcess を用いて同様な結果を得ることができます。いくつかのサンプルを以下に示します。 | ||
+ | |||
+ | Important: 実行ファイルへのフルパスを明示する必要があります。例えば、'cp' ではなく '/bin/cp' のように。 もしもプログラムにパスが通っていたなら、LCLの[[doc:lcl/fileutil/index.html|FileUtil]] ユニットから、[[doc:lcl/fileutil/finddefaultexecutablepath.html|FindDefaultExecutablePath]] 関数を使用できます。 | ||
===単純な見本=== | ===単純な見本=== | ||
− | // | + | <pascal> |
+ | // これはどのようにして外部プログラムを実行するかを示すデモプログラムです | ||
program launchprogram; | program launchprogram; | ||
− | // | + | // 関数や手続きを使用するためにファイルをインクルードします |
− | |||
uses | uses | ||
Classes, SysUtils, Process; | Classes, SysUtils, Process; | ||
− | // "TProcess" | + | // "TProcess"型の変数"AProcess"を定義します |
− | |||
− | |||
var | var | ||
AProcess: TProcess; | AProcess: TProcess; | ||
− | // | + | // ここからプログラムが開始します |
begin | begin | ||
− | // | + | // TProcessオブジェクトを生成し、変数AProcessにアサインします |
− | |||
AProcess := TProcess.Create(nil); | AProcess := TProcess.Create(nil); | ||
// AProcessに実行するコマンドを伝えます | // AProcessに実行するコマンドを伝えます | ||
− | // | + | // FreePascalコンパイラを実行してみましょう |
AProcess.CommandLine := 'ppc386 -h'; | AProcess.CommandLine := 'ppc386 -h'; | ||
− | // | + | // プログラムを走らせるときの、オプションを定義しましょう |
− | // | + | // このオプションは、実行した外部プログラムが停止するまで、 |
− | // | + | // このプログラムが動かないようにします vvvvvvvvvvvvvv |
AProcess.Options := AProcess.Options + [poWaitOnExit]; | AProcess.Options := AProcess.Options + [poWaitOnExit]; | ||
− | // | + | // AProcessは実行するコマンドラインを知っています |
AProcess.Execute; | AProcess.Execute; | ||
Line 53: | Line 59: | ||
AProcess.Free; | AProcess.Free; | ||
end. | end. | ||
+ | </pascal> | ||
− | + | おめでとう!自分のプログラムから外部プログラムを実行する方法を学べました。 | |
===より向上した見本=== | ===より向上した見本=== | ||
− | + | <delphi> | |
+ | それでは、どのようにして実行した外部プログラムの出力を読めばよいでしょう? | ||
− | + | 先ほどの例を拡張してみましょう。 | |
− | // | + | // これはどのようにして外部プログラムを実行させ、その出力を読むかを |
− | // | + | // 示すデモプログラムです。 |
program launchprogram; | program launchprogram; | ||
− | // | + | // 関数や手続きを使用するためにファイルをインクルードします |
− | |||
uses | uses | ||
Classes, SysUtils, Process; | Classes, SysUtils, Process; | ||
− | // | + | // "TProcess"型の変数"AProcess"を定義します |
− | // | + | // また、プログラムの出力からデータを読み取り、 |
− | // | + | // 保存する TStringList を追加します。 |
− | |||
var | var | ||
AProcess: TProcess; | AProcess: TProcess; | ||
AStringList: TStringList; | AStringList: TStringList; | ||
− | // | + | // ここからプログラムが開始します |
begin | begin | ||
− | // | + | // TProcessオブジェクトを生成し、変数AProcessにアサインします |
− | |||
AProcess := TProcess.Create(nil); | AProcess := TProcess.Create(nil); | ||
Line 88: | Line 93: | ||
// AProcessに実行するコマンドを伝えます | // AProcessに実行するコマンドを伝えます | ||
− | // | + | // FreePascalコンパイラを実行してみましょう |
AProcess.CommandLine := 'ppc386 -h'; | AProcess.CommandLine := 'ppc386 -h'; | ||
− | // | + | // プログラムを走らせるときの、オプションを定義しましょう |
− | // | + | // このオプションは、実行した外部プログラムが停止するまで、 |
− | // | + | // このプログラムが動かないようにします |
− | // | + | // また、ファイルの出力を読みたいことを伝えます |
AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes]; | AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes]; | ||
− | // | + | // AProcessは実行するコマンドラインを知っています |
AProcess.Execute; | AProcess.Execute; | ||
// ppc386が停止するまで、次にいきません | // ppc386が停止するまで、次にいきません | ||
− | // | + | // プログラムの出力を、TStringListに読み込みます。 |
AStringList.LoadFromStream(AProcess.Output); | AStringList.LoadFromStream(AProcess.Output); | ||
Line 108: | Line 113: | ||
AStringList.SaveToFile('output.txt'); | AStringList.SaveToFile('output.txt'); | ||
− | // | + | // ファイルが保存されたので、 |
// TStringListとTProcessを開放します | // TStringListとTProcessを開放します | ||
AStringList.Free; | AStringList.Free; | ||
AProcess.Free; | AProcess.Free; | ||
end. | end. | ||
+ | </delphi> | ||
===巨大な出力を読み取る=== | ===巨大な出力を読み取る=== | ||
− | + | 前述したサンプルでは、外部プログラムの終了まで待ち、その後に外部プログラムの出力内容を読み取りました。 | |
+ | ここで、外部プログラムが大量のデータを出力すると想定した場合、パイプが満杯になるとパイプの内容が読み出されるまで外部プログラムはストップします。 | ||
+ | しかし、外部プログラムが終わるまでは、読み出し手のプログラムはパイプの内容を読み出せないため、袋小路に陥ります。 | ||
− | + | 後述するサンプルでは poWaitOnExitを使わず、プログラムの実行中に出力から読むようにしました。出力はメモリストリームに保存され、追って TStringList に読み出されます。 | |
+ | <delphi> | ||
program procoutlarge; | program procoutlarge; | ||
{ | { | ||
Line 142: | Line 151: | ||
begin | begin | ||
− | // | + | // 出力サイズが不明であり、poWaintOnExit は使用できません。 |
− | // | + | // Linux では出力パイプの大きさは2KBであり、 |
− | // | + | // データの大きさがこれ以上である場合、行き詰まります。 |
− | |||
− | |||
// | // | ||
− | // | + | // 一時的なMemorystreamが出力バッファとして使われます。 |
M := TMemoryStream.Create; | M := TMemoryStream.Create; | ||
Line 202: | Line 209: | ||
P.Free; | P.Free; | ||
M.Free; | M.Free; | ||
− | end. | + | end.</delphi> |
+ | |||
+ | === TProcessの出力と入力を使用する === | ||
+ | [https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/examples/process Lazarus-CCR SVN]のprocessdemoを参照せよ | ||
+ | |||
+ | === TProcess利用のヒント === | ||
+ | cross-platform programを作成する場合、"{$IFDEF}s" and "{$ENDIF}s" 命令を利用することで、OSに応じて commandline を変更できる。 | ||
+ | |||
+ | Example: | ||
+ | <delphi> {...} | ||
+ | AProcess:TProcess.Create(nil) | ||
+ | {$IFDEF WIN32} | ||
+ | AProcess.CommandLine := 'calc.exe'; //Windows Calc | ||
+ | {$ENDIF} | ||
+ | {$IFDEF LINUX} | ||
+ | AProcess.CommandLine := 'kcalc'; //KDE Calc | ||
+ | {$ENDIF} | ||
+ | AProcess.Execute; //右の書き方も可能 AProcess.Active:=True | ||
+ | {...}</delphi> | ||
+ | |||
+ | === aspell processによる"talking"のサンプル === | ||
− | + | [http://pasdoc.sourceforge.net/ pasdoc]のソースコードでは、二つのユニット - 複数のパイプを介してaspell processを実行することで、"talking"によってスペルチェックを行う - が見つかる。 | |
− | [http://pasdoc.sourceforge.net/ pasdoc] | + | * [http://pasdoc.svn.sourceforge.net/viewvc/*checkout*/pasdoc/trunk/source/component/PasDoc_ProcessLineTalk.pas PasDoc_ProcessLineTalk.pas unit] は、TProcessを継承するTProcessLineTalkクラスを実装している。これによって、line-by-line basisの上の他のプロセスとの通信が容易となる。 |
− | * [http:// | + | * [http://pasdoc.svn.sourceforge.net/viewvc/*checkout*/pasdoc/trunk/source/component/PasDoc_Aspell.pas PasDoc_Aspell.pas units] は、TAspellProcessクラスを実装している。TProcessLineTalkのインスタンスの下にあり、aspell processを実行することで、aspellを実行し、通信これは、スペルチェックを行う。 |
− | + | that performs spell-checking by using underlying TProcessLineTalk instance to execute aspell and communicate with running aspell process. | |
− | + | いずれもユニットも残りのpasdoc sourcesから独立しているため、TProcessを使用した実例 - パイプを介して他のプログラムと実行、通信する実例 - として役に立つだろう。 |
Revision as of 15:33, 9 July 2008
│
Deutsch (de) │
English (en) │
español (es) │
français (fr) │
italiano (it) │
日本語 (ja) │
Nederlands (nl) │
polski (pl) │
português (pt) │
русский (ru) │
slovenčina (sk) │
中文(中国大陆) (zh_CN) │
概要
外部のプログラムを動かすにはいくつかの方法がありますが、このチュートリアルでは TProcessを中心に紹介しています。
Delphiで ShellExecute や WinExec を使っていたなら、 FPC/Lazarusにおける代替としてTProcessを使用するようにしましょう。(TProcess は cross-platformであり、Linuxでも有効です。)
Note: FPC/Lazarus は ShellExecute 及び WinExec をサポートしていますが、win32のみで有効です。 あなたのプログラムが cross-platformを目標にしているなら、TProcessの使用が最善です!
SysUtils.ExecuteProcess
パイプや他のコントロールから成るフォームを使用しない場合、最も簡単な方法は、 SysUtils.ExecuteProcess('/full/path/to/binary',['arg1','arg2'])を使用することです。
TProcess
あなたはTProcessを使って外部プログラムを実行することができます。TProcessを使うメリットは次の通りです。
- プラットホームに依存しない
- stdinからの読み込み、stdoutへの書き込みが可能
Note: TProcess は terminal/shell ではないので、直接スクリプトを実行することや、出力結果をリダイレクトすることは出来ません。 しかし、 TProcess を用いて同様な結果を得ることができます。いくつかのサンプルを以下に示します。
Important: 実行ファイルへのフルパスを明示する必要があります。例えば、'cp' ではなく '/bin/cp' のように。 もしもプログラムにパスが通っていたなら、LCLのFileUtil ユニットから、FindDefaultExecutablePath 関数を使用できます。
単純な見本
<pascal>
// これはどのようにして外部プログラムを実行するかを示すデモプログラムです program launchprogram; // 関数や手続きを使用するためにファイルをインクルードします uses Classes, SysUtils, Process; // "TProcess"型の変数"AProcess"を定義します var AProcess: TProcess; // ここからプログラムが開始します begin // TProcessオブジェクトを生成し、変数AProcessにアサインします AProcess := TProcess.Create(nil); // AProcessに実行するコマンドを伝えます // FreePascalコンパイラを実行してみましょう AProcess.CommandLine := 'ppc386 -h'; // プログラムを走らせるときの、オプションを定義しましょう // このオプションは、実行した外部プログラムが停止するまで、 // このプログラムが動かないようにします vvvvvvvvvvvvvv AProcess.Options := AProcess.Options + [poWaitOnExit]; // AProcessは実行するコマンドラインを知っています AProcess.Execute; // ppc386が停止するまで、これは実行されません。 AProcess.Free; end.
</pascal>
おめでとう!自分のプログラムから外部プログラムを実行する方法を学べました。
より向上した見本
<delphi> それでは、どのようにして実行した外部プログラムの出力を読めばよいでしょう?
先ほどの例を拡張してみましょう。
// これはどのようにして外部プログラムを実行させ、その出力を読むかを // 示すデモプログラムです。 program launchprogram; // 関数や手続きを使用するためにファイルをインクルードします uses Classes, SysUtils, Process; // "TProcess"型の変数"AProcess"を定義します // また、プログラムの出力からデータを読み取り、 // 保存する TStringList を追加します。 var AProcess: TProcess; AStringList: TStringList; // ここからプログラムが開始します begin // TProcessオブジェクトを生成し、変数AProcessにアサインします AProcess := TProcess.Create(nil); // TStringListオブジェクトを生成します AStringList := TStringList.Create; // AProcessに実行するコマンドを伝えます // FreePascalコンパイラを実行してみましょう AProcess.CommandLine := 'ppc386 -h'; // プログラムを走らせるときの、オプションを定義しましょう // このオプションは、実行した外部プログラムが停止するまで、 // このプログラムが動かないようにします // また、ファイルの出力を読みたいことを伝えます AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes]; // AProcessは実行するコマンドラインを知っています AProcess.Execute; // ppc386が停止するまで、次にいきません // プログラムの出力を、TStringListに読み込みます。 AStringList.LoadFromStream(AProcess.Output); // ファイルに保存します AStringList.SaveToFile('output.txt'); // ファイルが保存されたので、 // TStringListとTProcessを開放します AStringList.Free; AProcess.Free; end.
</delphi>
巨大な出力を読み取る
前述したサンプルでは、外部プログラムの終了まで待ち、その後に外部プログラムの出力内容を読み取りました。 ここで、外部プログラムが大量のデータを出力すると想定した場合、パイプが満杯になるとパイプの内容が読み出されるまで外部プログラムはストップします。 しかし、外部プログラムが終わるまでは、読み出し手のプログラムはパイプの内容を読み出せないため、袋小路に陥ります。
後述するサンプルでは poWaitOnExitを使わず、プログラムの実行中に出力から読むようにしました。出力はメモリストリームに保存され、追って TStringList に読み出されます。
<delphi>
program procoutlarge; { Copyright (c) 2004 by Marc Weustink This example is creeated in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. } uses Classes, Process, SysUtils; const READ_BYTES = 2048; var S: TStringList; M: TMemoryStream; P: TProcess; n: LongInt; BytesRead: LongInt; begin // 出力サイズが不明であり、poWaintOnExit は使用できません。 // Linux では出力パイプの大きさは2KBであり、 // データの大きさがこれ以上である場合、行き詰まります。 // // 一時的なMemorystreamが出力バッファとして使われます。 M := TMemoryStream.Create; BytesRead := 0; P := TProcess.Create(nil); P.CommandLine := 'ppc386 -va bogus.pp'; P.Options := [poUsePipes]; WriteLn('-- executing --'); P.Execute; while P.Running do begin // メモリ空間を確保します M.SetSize(BytesRead + READ_BYTES); // 読んでいきます n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES); if n > 0 then begin Inc(BytesRead, n); Write('.') end else begin // データがないときは100 ms待ちます Sleep(100); end; end; // 読み込みの最終段階です repeat // メモリ空間を確保します M.SetSize(BytesRead + READ_BYTES); // 読んでいきます n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES); if n > 0 then begin Inc(BytesRead, n); Write('.'); end; until n <= 0; if BytesRead > 0 then WriteLn; M.SetSize(BytesRead); WriteLn('-- executed --'); S := TStringList.Create; S.LoadFromStream(M); WriteLn('-- linecount = ', S.Count, ' --'); for n := 0 to S.Count - 1 do begin WriteLn('| ', S[n]); end; WriteLn('-- end --'); S.Free; P.Free; M.Free; end.</delphi>
TProcessの出力と入力を使用する
Lazarus-CCR SVNのprocessdemoを参照せよ
TProcess利用のヒント
cross-platform programを作成する場合、"{$IFDEF}s" and "{$ENDIF}s" 命令を利用することで、OSに応じて commandline を変更できる。
Example: <delphi> {...}
AProcess:TProcess.Create(nil) {$IFDEF WIN32} AProcess.CommandLine := 'calc.exe'; //Windows Calc {$ENDIF} {$IFDEF LINUX} AProcess.CommandLine := 'kcalc'; //KDE Calc {$ENDIF} AProcess.Execute; //右の書き方も可能 AProcess.Active:=True {...}</delphi>
aspell processによる"talking"のサンプル
pasdocのソースコードでは、二つのユニット - 複数のパイプを介してaspell processを実行することで、"talking"によってスペルチェックを行う - が見つかる。
- PasDoc_ProcessLineTalk.pas unit は、TProcessを継承するTProcessLineTalkクラスを実装している。これによって、line-by-line basisの上の他のプロセスとの通信が容易となる。
- PasDoc_Aspell.pas units は、TAspellProcessクラスを実装している。TProcessLineTalkのインスタンスの下にあり、aspell processを実行することで、aspellを実行し、通信これは、スペルチェックを行う。
that performs spell-checking by using underlying TProcessLineTalk instance to execute aspell and communicate with running aspell process.
いずれもユニットも残りのpasdoc sourcesから独立しているため、TProcessを使用した実例 - パイプを介して他のプログラムと実行、通信する実例 - として役に立つだろう。