Difference between revisions of "Executing External Programs/ja"

From Free Pascal wiki
Jump to navigationJump to search
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を使用することによるメリットは次の通りです。
+
あなたは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"型の"AProcess"
+
  // "TProcess"型の変数"AProcess"を定義します
// という変数を定義しておきます
 
 
 
  var  
 
  var  
 
   AProcess: TProcess;
 
   AProcess: TProcess;
 
   
 
   
  // ここからプログラムが走ります
+
  // ここからプログラムが開始します
 
  begin
 
  begin
   // TProcessオブジェクトを生成します
+
   // TProcessオブジェクトを生成し、変数AProcessにアサインします
  // そしてそれを変数AProcessにアサインします
 
 
   AProcess := TProcess.Create(nil);
 
   AProcess := TProcess.Create(nil);
 
   
 
   
 
   // AProcessに実行するコマンドを伝えます
 
   // AProcessに実行するコマンドを伝えます
   // FreePascalコンパイラを走らせてみます
+
   // FreePascalコンパイラを実行してみましょう
 
   AProcess.CommandLine := 'ppc386 -h';
 
   AProcess.CommandLine := 'ppc386 -h';
 
   
 
   
   // ここでプログラムを走らせるときの、オプションを定義します
+
   // プログラムを走らせるときの、オプションを定義しましょう
   // このオプションは、自分のプログラムを走らせたプログラムが
+
   // このオプションは、実行した外部プログラムが停止するまで、
   // 停止するまで動かないようにします        vvvvvvvvvvvvvv
+
   // このプログラムが動かないようにします          vvvvvvvvvvvvvv
 
   AProcess.Options := AProcess.Options + [poWaitOnExit];
 
   AProcess.Options := AProcess.Options + [poWaitOnExit];
 
   
 
   
   // ここでAProcessが走ります。
+
   // 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;
 
   
 
   
  // This is defining the var "AProcess" as a variable
+
  // "TProcess"型の変数"AProcess"を定義します
  // of the type "TProcess"
+
  // また、プログラムの出力からデータを読み取り、
  // Also now we are adding a TStringList to store the
+
  // 保存する TStringList を追加します。
// data read from the programs output.
 
 
  var  
 
  var  
 
   AProcess: TProcess;
 
   AProcess: TProcess;
 
   AStringList: TStringList;
 
   AStringList: TStringList;
 
   
 
   
  // ここからプログラムが走ります
+
  // ここからプログラムが開始します
 
  begin
 
  begin
   // TProcessオブジェクトを生成します
+
   // TProcessオブジェクトを生成し、変数AProcessにアサインします
  // そしてそれを変数AProcessにアサインします
 
 
   AProcess := TProcess.Create(nil);
 
   AProcess := TProcess.Create(nil);
 
   
 
   
Line 88: Line 93:
 
   
 
   
 
   // AProcessに実行するコマンドを伝えます
 
   // AProcessに実行するコマンドを伝えます
   // FreePascalコンパイラを走らせてみます
+
   // FreePascalコンパイラを実行してみましょう
 
   AProcess.CommandLine := 'ppc386 -h';
 
   AProcess.CommandLine := 'ppc386 -h';
 
   
 
   
   // ここでプログラムを走らせるときの、オプションを定義します
+
   // プログラムを走らせるときの、オプションを定義しましょう
   // このオプションは、自分のプログラムを走らせたプログラムが
+
   // このオプションは、実行した外部プログラムが停止するまで、
   // 停止するまで動かないようにします
+
   // このプログラムが動かないようにします
   // また、ファイルの出力を読めるようにします
+
   // また、ファイルの出力を読みたいことを伝えます
 
   AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
 
   AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
 
   
 
   
   // AProcessはコマンドライン上で実行されるように実行されます
+
   // AProcessは実行するコマンドラインを知っています
 
   AProcess.Execute;
 
   AProcess.Execute;
 
    
 
    
 
   // ppc386が停止するまで、次にいきません
 
   // ppc386が停止するまで、次にいきません
 
   
 
   
   // ここでTStringListに出力を読み込みます。
+
   // プログラムの出力を、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を使って読み出すことができます。
+
後述するサンプルでは poWaitOnExitを使わず、プログラムの実行中に出力から読むようにしました。出力はメモリストリームに保存され、追って TStringList に読み出されます。
  
 +
<delphi>
 
  program procoutlarge;
 
  program procoutlarge;
 
  {
 
  {
Line 142: Line 151:
 
   
 
   
 
  begin
 
  begin
   // poWaintOnExitを使うことはできませんので
+
   // 出力サイズが不明であり、poWaintOnExit は使用できません。
   // 出力のサイズがわかりません。
+
   // Linux では出力パイプの大きさは2KBであり、
   // Linuxにおいては出力パイプの大きさが2kbです。
+
   // データの大きさがこれ以上である場合、行き詰まります。
  // これ以上の大きさのデータを読むことはこの方法ではできません。
 
  // 従って行き詰まります。
 
 
   //
 
   //
   // Memorystreamは出力バッファとして使います。
+
   // 一時的な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"のサンプル ===
  
=== aspellによる"talking"の見本 ===
+
[http://pasdoc.sourceforge.net/ pasdoc]のソースコードでは、二つのユニット - 複数のパイプを介してaspell processを実行することで、"talking"によってスペルチェックを行う - が見つかる。
  
[http://pasdoc.sourceforge.net/ pasdoc] ソースコードではあなたはふたつのユニットを発見し、aspellプロセスを実行しパイプを介してスペルチェックを"talking"によって行っています。
+
* [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://cvs.sourceforge.net/viewcvs.py/*checkout*/pasdoc/pasdoc/source/component/PasDoc_ProcessLineTalk.pas PasDoc_ProcessLineTalk.pas unit] implements TProcessLineTalk class, descendant of TProcess, that can be easily used to talk with any process on a line-by-line basis.
+
* [http://pasdoc.svn.sourceforge.net/viewvc/*checkout*/pasdoc/trunk/source/component/PasDoc_Aspell.pas PasDoc_Aspell.pas units] は、TAspellProcessクラスを実装している。TProcessLineTalkのインスタンスの下にあり、aspell processを実行することで、aspellを実行し、通信これは、スペルチェックを行う。
  
* [http://cvs.sourceforge.net/viewcvs.py/*checkout*/pasdoc/pasdoc/source/component/PasDoc_Aspell.pas PasDoc_Aspell.pas units] implements TAspellProcess class, that performs spell-checking by using underlying TProcessLineTalk instance to execute aspell and communicate with running aspell process.
+
that performs spell-checking by using underlying TProcessLineTalk instance to execute aspell and communicate with running aspell process.
  
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.
+
いずれもユニットも残りの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)

日本語版メニュー
メインページ - Lazarus Documentation日本語版 - 翻訳ノート - 日本語障害情報

概要

外部のプログラムを動かすにはいくつかの方法がありますが、このチュートリアルでは 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を使用した実例 - パイプを介して他のプログラムと実行、通信する実例 - として役に立つだろう。