外部のプログラムとの連携

SPAWN

IDLから外部のプログラムを実行するにはいくつか方法がある。IDLプロンプトから単に外部プログラムを実行するだけなら $を使う。Windows では新たに開いたコマンドプロンプトの中でプログラムが実行されるが、プログラム終了後すぐに閉じてしまう。

  1. $ ; シェルが起動する (Unixのコマンドライン版IDLのみ)
  2. $ls
  3. $\ls ; ls --color 等のaliasが定義されていて、lsの結果にエスケープシーケンスが含まれる場合
  4. $cp file1 file2

IDLのプログラム中から実行する場合や、外部プログラムとの間でデータをやりとりする場合には SPAWN を使う。noshellキーワードを指定すると、新たにシェルを起動しないため速くなる。

  1. spawn ; シェルが起動する
  2. spawn, 'ls -l' ; lsの結果を画面に表示する
  3. spawn, 'ls -l', result ; lsの結果(標準出力)を変数result(文字列の配列)に格納する
  4. spawn, 'ls -l no_such_file', result, errresult ; 3番目の引数を指定すると標準エラー出力が格納される
  5. spawn, ['ls', '-l'], result, /noshell ; noshellキーワードを指定すると、シェルを経由せずに実行するため速い。ただし、シェルの内部コマンドは使えない。また、引数は文字列の配列として渡す必要がある。
  6. spawn, 'dir', result, /hide ; Windowsでは hide キーワードを指定すると、コマンドプロンプトが最小化された状態で実行される
  7. spawn, 'sleep 10 &' ; Unixで子プロセスを非同期に実行する

基本的なファイル操作はなるべくIDL自身がもつファイル操作機能(FILE_SEARCHFILE_COPYFILE_MKDIRなど)を使うほうが移植性の高いプログラムとなる。

パイプを用いてデータをやりとりする

子プロセスとして起動した外部プログラムとの間でパイプによるデータのやりとりを行うことができる。

子プロセスの標準入力・出力両方を同時に利用する場合にはデッドロックに注意。出力のバッファリングはオフにしておく(C言語の場合、setbuf(stdout, NULL); )。IDLでは非同期I/Oを使うのが難しいので、標準入力・出力両方で同時にパイプのバッファサイズ(x64のLinuxのデフォルトは64KiB)を超える大量のデータを扱う(例えば外部プログラムをフィルタとして使う)ことは困難だと思われる。

子プロセスが終了後に、WRITEUでパイプに書き込もうとするとエラーとなり、% WRITEU: Error encountered writing to file. Unit: 100, File: <prog> Broken pipeというメッセージが表示される。

データをxzで圧縮しながらファイルに保存した後、それを展開しながら読み込む例:

  1. ; xzで圧縮したデータを書き込む
  2. data = bytscl(dist(1000))
  3. spawn, 'xz > file.xz', unit=unit
  4. writeu, unit, data
  5. free_lun, unit
  1. ; xzで圧縮されたデータを読み込む
  2. data2 = bytarr(1000,1000)
  3. spawn, ['unxz', '-c', 'file.xz'], unit=unit, /noshell
  4. readu, unit, data2
  5. free_lun, unit

CALL_EXTERNAL

IDLから外部のダイナミックライブラリ内の関数を呼び出すことができる。ただし、引数は特殊な方法で渡されるため、IDLから呼び出されることを想定されていない関数を利用する場合には、引数の変換を行うラッパー関数が必要になる。AUTO_GRUEキーワードをつけることで、自動的にラッパー関数を生成しコンパイルしてくれる。以下はすべてx86_64のLinux環境での例。

  1. ; 引数を取らず、32bit整数を返す関数を呼ぶ (getpid(2)は呼び出し元のプロセスIDを返す)
  2. pid = call_external('libc.so.6', 'getpid')
  3. ; 2つの32bit整数の引数をとり、32bit整数を返す関数を呼ぶ (kill(2)で2つ目の引数に0を指定すると何も起こらないので、プロセスの存在確認に使える)
  4. result = call_external('libc.so.6', 'kill', pid, 0L, /auto_glue, /all_value)
  5. ; 1つの倍精度実数の引数をとり、倍精度実数を返す関数を呼ぶ (平方根)
  6. result = call_external('libm.so.6', 'sqrt', 2d, /auto_glue, /all_value, /d_value)

AUTO_GRUEキーワードにより作成されるラッパー関数を含むライブラリは、IDLの実行バイナリがあるディレクトリのce_glueサブディレクトリ(標準では存在しない)、または !MAKE_DLL.COMPILE_DIRECTORYシステム変数が指すディレクトリに保存される。

WRITE_WRAPPERキーワードを指定することでラッパー関数のソースコードを出力できる。WRITE_WRAPPERキーワードをつけた場合、実際に関数の呼び出しは行われない。

  1. result = call_external('libm.so.6', 'sqrt', 2d, write_wrapper = 'wrapper.c', /all_value, /d_value)

IDLから呼べるライブラリを作る

IDLのインストールディレクトリ(IDL_DIR)以下のexternal/call_externalにCALL_EXTERNALのサンプルプログラムがある。

  1. # Intel Fortran Compilerの場合
  2. ifort -shared -fpic example.f -o example.so
  3. # GFortranの場合
  4. gfortran -shared example1.f -o example1.so -fPIC
西田圭佑 (NISHIDA Keisuke)
nishida at kwasan.kyoto-u.ac.jp
$Id: external.html,v 1.5 2020/06/03 05:33:40 nishida Exp nishida $