Next: gauche.record
- レコード型, Previous: gauche.partcont
- 部分継続, Up: ライブラリモジュール - Gauche拡張モジュール [Contents][Index]
gauche.process
- 高レベルプロセスインタフェースこのモジュールは、低レベルなシステムコールの上に実装された、 プロセス制御の高レベル API を提供します。 また、このモジュールは、サブプロセスに情報を送ったり、サブプロセスから 情報を受け取ったりするのに便利な「プロセスポート」を提供します。
• サブプロセスの実行: | ||
• プロセスパイプラインの実行: | ||
• Process object: | ||
• Process ports: | ||
• Process connection: |
Next: プロセスパイプラインの実行, Previous: gauche.process
- 高レベルプロセスインタフェース, Up: gauche.process
- 高レベルプロセスインタフェース [Contents][Index]
{gauche.process}
サブプロセスで、cmd/argsに与えられたコマンドと引数を実行します。
cmd/args引数はリストで、そのcar
がコマンド名を、
cdr
がコマンドラインに渡す引数を指定します。
コマンド名がスラッシュを含んでいた場合、それは実行可能ファイルへの
パス名と解釈されます。そうでなければ、コマンド名がPATH
環境変数
にあるディレクトリから探されます。
cmd/argsの各要素は、x->string
で文字列に変換されます。
do-process
は常にサブプロセスが終了するまで待ち、正常終了した場合(終了ステータスが
0の場合)に#t
を返します。サブプロセスが異常終了した場合の
ふるまいはキーワード引数on-abnormal-exitによって次のように決定されます。
#f
の場合(デフォルト)、単に#f
が返されます。
これはcall-with-input-process
などのデフォルト(エラーを投げる)とは
異なっていることに注意してください。do-process
は、条件分岐と組み合わせて
シェルスクリプトのif
コマンドのように使うことを想定しています。
(if (do-process command) then-expr else-expr)
は
シェルのif command; then then-command; else else-command; fi
のように動作します。
:error
の場合、<process-abnormal-exit>
エラーが投げられます。
:exit-code
の場合、サブプロセスがゼロ以外の終了ステータスで終了したなら、
その終了コード (サブプロセスのexit()
に渡された整数値) を整数で返します。
サブプロセスがシグナルにより終了した場合は
<process-abnormal-exit>
エラーが投げられることに注意してください。
do-process!
はdo-process
とほぼ同じですが、
プロセスが0以外の終了ステータスを返した場合に<process-abnormal-exit>
エラーを
投げます。do-process
のon-abnormal-exit
に:error
を
渡した場合の振る舞いと同じです。シェルスクリプト的な仕事で、
コマンドが失敗したらそこで終わってよいといった場合に便利です。
run-process
はデフォルトでサブプロセスを並行して走らせます。
つまり、直ちに返ってきます。返り値は<process>
オブジェクトで、それを
用いてサブプロセスの状態を追跡できます
(Process object参照)。
例えば、次の式はls -al
を実行します。
(do-process '(ls -al))
ls
コマンドが何らかの理由で失敗しない限り、
ls -al
の出力が表示されてから、#t
が返されるでしょう。
do-process
はコマンドの成功失敗を真偽値で返すので、
シェルの&&
や||
オペレータでコマンドを組み合わせるのと同じことが
and
やor
でできます。
;; shell: make && make -s check (and (do-process '(make)) (do-process '(make -s check))) ;; shell: mv x.tmp x.c || rm -f x.tmp (or (do-process '(mv x.tmp x.c)) (do-process '(rm -f x.tmp)))
代わりにrun-process
を使った場合は、ls -al
の終了を待たずに
<process>
オブジェクトが返されます。
次の式をREPLで実行したなら、おそらく
ls
の出力の前に<process>
オブジェクトが表示されるでしょう。
(run-process '(ls -al))
戻り値の<process>
オブジェクトを保持しておいて、後で
process-wait
を呼ぶことによって子プロセスの終了を待つことができます。
process-wait
についてはProcess objectで説明します。
(let1 p (run-process '(ls -al)) ... do some other work ... (process-wait p))
run-process
に、プロセス終了を待つように指示することもできます。
その場合、run-process
は内部でprocess-wait
を呼びます。
do-process
は成功/失敗しかわからないのに対し、
プロセスの終了ステータスを後で調べたい場合に便利です。
ところで、-i
は虚数として読まれることに注意してください。
-i
を引数として渡したい場合は文字列にするか、|-i|
のように
エスケープしてシンボルにする必要があります。
(run-process '(ls "-i"))
注:外部プロセスを走らせる方法にはもうひとつ、sys-system
があります。
こちらはコマンド行を単一の文字列で渡し、シェルを呼び出して解釈させます
(プロセス管理参照)。
したがって、入出力のリダイレクトや、パイプでつないだコマンド等もそのまま渡せます。
書き捨てのスクリプトを素早く書くときはそちらの方が便利でしょう。
一方、sys-system
を使う場合には、コマンドの引数を実行時に可変にしたい場合に
きちんとエスケープされているかどうかを確認したり (実はgauche.process
モジュール中にそれをする関数があります。下のshell-escape-string
を
参照してください)、またsys-system
がsystem(3)
経由で
呼び出す/bin/sh
のプラットフォーム間の違いを気にしたりする必要があります。
原則として、sys-system
の使用は固定コマンドを呼び出す簡単な場合に
止めておき、他の仕事にはrun-process
や
do-process
を使うのが良いでしょう。
注:以前のバージョンのこの手続きは引数の取りかたが若干異なっており、
例えば(run-process "ls" "-al" :wait #t)
のように呼び出しました。
これはSTkとの互換性によるものです。現在でもこの呼び出しはサポートされていますが、
非推奨です。
子プロセスの実行を細かく制御するために、do-process
と
run-process
はたくさんの
キーワード引数を取ります。以下でそれらをいくつかのカテゴリに分類して説明します。
この引数はrun-process
のみに渡すことができます。
flag が真の場合、run-process
は内部で
process-wait
を呼びだし、サブプロセスが終了するまで待ちます。
そうでなければ、サブプロセスは非同期に実行され、run-process
は
すぐに返ります。これがデフォルトの振る舞いになります。
サブプロセスが非同期に実行している場合、その終了ステータスを回収するために
適切なタイミングで process-wait
を呼ぶことは、呼び出し側の責任であることに
注意してください。
;; This returns after wget terminates. (define p (run-process '(wget http://practical-scheme.net/) :wait #t)) ;; Check the exit status (let1 st (process-exit-status p) (cond [(sys-wait-exited? st) (print "wget exited with status " (sys-wait-exit-status st))] [(sys-wait-signaled? st) (print "wget interrupted by signal " (sys-wait-termsig st))] [else (print "wget terminated with unknown status " st)]))
この引数はdo-process
のみに渡すことができます。
howが#f
の場合(デフォルト)、
サブプロセスが異常終了(ゼロ以外の終了ステータスを返)した場合は#f
を返します。
howが:error
なら、その場合はエラーを投げます。
flag が真の場合、do-process
と
run-process
はサブプロセスを実行するために
フォークします。これはデフォルトの振る舞いです。flag が偽の場合、
do-process
と
run-process
は直接 sys-exec
を呼ぶので、戻って来ません。
子プロセスのI/Oをどのようにリダイレクトするかを指定します。 各iospecは次の形式のいずれかです。ここでfd, fd0およびfd1は 子プロセスにおけるファイルディスクリプタを指定する非負の整数です。
(註:簡単にコマンドを走らせて結果を文字列で得たい場合は、
process-output->string
が使えます(Process ports参照)。
また、複数のコマンドをパイプでつなぎたい場合は
プロセスパイプラインの実行を見てください。)
(< fd source)
sourceは文字列、シンボル、キーワード:null
、整数、入力ポートのいずれかです。
文字列の場合、それはファイル名を指定します。そのファイルが読み込み用にオープンされ、 子プロセスはfdからその内容を読み込みます。指定されたファイルが存在しないか 読み込み用にオープンできなければエラーが報告されます。
シンボルの場合、一方向のパイプが作られ、その読み出し側の端が子プロセスのfdへと
接続されます。書き込み側の端は
(process-input process source)
を呼び出すことで入手できます。
:null
の場合、fdはシステムのヌルデバイスからの
読み込みになります。
整数の場合は、現プロセスの読み込み用ファイルディスクリプタを指定します。
そのファイルディスクリプタの読み込み元がdup
されて、子プロセスからはfdとして
見えます。
入力ポートの場合は、入力元のファイルディスクリプタがdup
されて
子プロセスのfdになります。ファイルディスクリプタを持たないポートを渡した
場合はエラーになります(ポート共通の操作のport-file-number
参照)。
(<< fd value)
(<<< fd obj)
valueまたはobjを子プロセスの入力ファイルディスクリプタfdへの 入力とします。
<<
を使う場合、valueは文字列かユニフォームベクタ
(see ユニフォームベクタ)でなければなりません。その内容がそのまま
子プロセスの入力へと送られます。ユニフォームベクタはバイナリデータを渡すのに便利です。
<<<
を使う場合、objには任意のSchemeオブジェクトを渡せます。
(write-to-string obj)
の結果の文字列が子プロセスの入力へと送られます。
(<& fd0 fd1)
子プロセスのファイルディスクリプタfd0が、
同じく子プロセスのファイルディスクリプタfd1が指しているものと
同じ入力を参照するようにします。
<との違いに注意してください。(< 3 0)
とすると、
現プロセスのstdin (ファイルディスクリプタ0) が子プロセスからはファイルディスクリプタ3
として見えるようになります。(<& 3 0)
とすると、子プロセスのファイルディスクリプタ3
は子プロセスのstdinと同じものを見るようになります (それは他のiospecによって
ファイル等にリダイレクトされているかもしれません)。
<&
の処理の順番について、下の方にある註も参照してください。
(> fd sink)
(>> fd sink)
sinkは文字列、シンボル、キーワード:null
、整数、あるいは
ファイル出力ポートでなければなりません。
文字列の場合、それはファイル名です。指定されたファイルが書き込み用にオープンされ、
子プロセスのファイルディスクリプタfdからそこに書き込むことができます。
指定のファイルが既に存在している場合、>
はファイルをまず空にするのに対し、
>>
はファイルの後に追加します。
>
と>>
の違いが出るのはsinkがファイルの場合のみです。
sinkがシンボルなら、一方向のパイプが作られ、その入力端が
子プロセスのfd
へと繋がれます。パイプに送られた子プロセスの出力を
読み出す入力ポートは、
(process-output process sink)
で得ることができます。
sinkが:null
なら、fdはシステムのヌルデバイスへの
書き出しになります。
整数の場合、それは現プロセスの書き込み用ファイルディスクリプタを
指定します。それがdup
されて子プロセスのfdとなります。
出力ポートの場合は、出力先のファイルディスクリプタがdup
されて
子プロセスのfdになります。ファイルディスクリプタを持たないポートを渡した
場合はエラーになります(ポート共通の操作のport-file-number
参照)。
(>& fd0 fd1)
子プロセスのファイルディスクリプタfd0が、子プロセスのファイルディスクリプタfd1
が参照するのと同じ出力先を参照するようにします。
>との違いに注意: (> 2 1)
は子プロセスのstderrが現プロセスのstdout
と同じところに送られるようにするのに対し、(>& 2 1)
は子プロセスのstderr
が子プロセスのstdoutに送られます (それは別のiospecによってリダイレクト
されているかもしれません)。
;; 子プロセスのstdoutとstderrを一緒にして読み込む (let1 p (run-process '(command arg) :redirects '((>& 2 1) (> 1 out))) (begin0 (port->string (process-output p 'out)) (process-wait p)))
註: 入力元や出力先にパイプを使う場合、同じ名前(シンボル)を複数の入出力に 指定することはできません。例えば次のコードはエラーとなります。
(run-process '(command) :redirects '((> 1 out) (> 2 out))) ; error!
複数の出力を一つの出力先にマージするには>&
を、
一つの入力元を複数の入力で読み込みには<&
を使ってください。
(run-process '(command) :redirects '((> 1 out) (>& 2 1)))
同じファイル名を複数の入出力に指定することは、Unixのシェル同様、可能です。 ただしその場合、ファイルはそれぞれの入出力ごとに別々にオープンされる ことに注意してください。例えば同じファイルを複数の出力からオープンして 書き込んだ場合、求める結果が得られないかもしれません(通常のファイルであれば、 一方の出力がもう一方の出力を上書きしてしまうでしょう)。
註: I/Oリダイレクト指定は、unixのシェルと違って、 全て同時に処理されます。つまり、以下の式はどちらも同じように、 stdoutとstderrをファイルoutに書き出す処理となります。
(run-process '(command arg) :redirects '((>& 2 1) (> 1 "out"))) (run-process '(command arg) :redirects '((> 1 "out") (>& 2 1)))
unixのシェルではリダイレクト指定は順に処理されるので、
次の二つのコマンドラインは異なる動作となります。最初の例では
子プロセスのstderrが現時点でのstdout (それは現プロセスのstdoutでもある)
へと向けられ、次に子プロセスのstdoutがファイルoutに向けられます。
従ってエラーメッセージは現プロセスのstdoutへと現れます。二番目の例では
最初に子プロセスのstdoutがファイルoutへ向けられるので、
2>&1
が処理される時にはstderrの行き先はやはりoutとなります。
$ command arg 2>&1 1>out $ command arg 1>out 2>&1
do-process
と
run-process
はredirects引数の順番にかかわらず
常に後者のように動作すると言ってもよいでしょう。
もし前者の例のように子プロセスのstderrだけを親プロセスのstdoutに
向けたいのであれば、次のように>
を使うことで実現できます。
(run-process '(command arg) :redirects '((> 2 1) (> 1 "out")))
サブプロセスの標準入出力を制御します。
source及びsinkは、文字列、キーワード :pipe
、
:null
、:merge
、整数のファイルディスクリプタ、もしくはシンボルです。
実のところ、これらはredirects引数の略記にすぎません。
:input x ≡ :redirects '((< 0 x)) :output x ≡ :redirects '((> 1 x)) :error x ≡ :redirects '((> 2 x))
キーワード:pipe
は互換性のためだけにサポートされています。
それぞれ、シンボルstdin
、stdout
、stderr
が
指定されたかのように振る舞います。
:input :pipe ≡ :redirects '((< 0 stdin)) :output :pipe ≡ :redirects '((> 1 stdout)) :error :pipe ≡ :redirects '((> 2 stderr))
すなわち、パイプが作成され、その一方が子プロセスの標準入出力のいずれかに接続されます。
もう一方の端は(process-input process)
、
(process-output process)
および
(process-error process)
によって得ることができます。
(process-input
, process-output
はname引数が
省略されるとそれぞれstdin
, stdout
をデフォルトとし、
また(process-error p)
は(process-output p 'stderr)
と
等価だからです)
キーワード:merge
は:error
キーワード引数のみに有効で、
:redirects '((>& 2 1))
の略記になります。
すなわち、子プロセスのstderrを子プロセスのstdoutにマージします。
引数の意味の詳しい説明については上のredirectsの項を参照してください。
directoryに文字列が与えられた場合、
そのディレクトリが起動されるプロセスのワーキングディレクトリとなります。
#f
が与えられた場合はの引数は何もしません。
文字列か#f
以外が与えられた場合、もしくは文字列が存在するディレクトリの
名前でない場合はエラーが報告されます。
hostキーワード引数も与えられている場合、この引数は リモートプロセスのワーキングディレクトリを指定します。
註: do-process
と
run-process
はdirectoryが有効な値であることを事前に
チェックしますが、実際のchdir(2)
はexec(2)
の直前に
行われます。事前のチェックにもかかわらずchdir
が失敗する可能性が
あります。その時点では呼び出し元にエラーを伝える
確実な方法が無いため、Gaucheは標準エラー出力にメッセージを印字して
exitします。頑健なプログラムを書く場合、そのようなケースにも留意して下さい。
maskは<sys-sigset>
のインスタンス、整数のリスト、
あるいは#f
でなければなりません。
<sys-sigset>
のインスタンスである場合、それが実行する
プロセスのシグナルマスクになります。整数のリストの場合は各整数が
マスクすべきシグナル番号とみなされます。マルチスレッドアプリケーションで
run-process
を使う場合はシグナルマスクを適切に設定することが重要です。
sys-exec
の説明を参照して下さい (プロセス管理)。
hostキーワード引数が与えられている場合は、この引数は
ローカル側のプロセス(ssh
)のみのシグナルマスクをセットします。
真の値が渡されると、作られるプロセスは親プロセスのプロセスグループから
切り離され、独自のプロセスグループを作ります。
デーモンプロセスを作る際に便利です。detached引数の詳しい動作については、
sys-fork-and-exec
を見てください (プロセス管理参照)。
この引数は、commandをリモートホストで実行させるのに使います。
hostspecの完全な構文はprotocol:user@hostname:port
で、
protocol:、user@
、:port
の部分は省略可能です。
protocolはリモートに接続するプロトコルを指定します。現在のところ
ssh
だけがサポートされており、また省略された場合もssh
が
使われます。userはリモートでのユーザ名を、hostnameは
リモートホスト名を指定します。portはprotocolのデフォルト
以外のポートを使いたい場合に指定します。
コマンドライン引数はリモートホスト上で解釈されます。 一方、I/Oリダイレクトはローカル側で処理されす。 例えば、次のコードはリモートマシンの/foo/barの内容を読み、 それをローカルのワーキングディレクトリ内のファイルbazへとコピーします。
(do-process '(cat "bar") :host "remote-host.example.com" :directory "/foo" :output "baz")
Next: Process object, Previous: サブプロセスの実行, Up: gauche.process
- 高レベルプロセスインタフェース [Contents][Index]
{gauche.process} 複数のプロセスでパイプラインを構成するための便利関数です。例:
(do-pipeline '((ls "src/") (grep "\\.c$") (wc -l)))
これはシェルのコマンドでのパイプラインls src/ | grep '\.c$' | wc -l
と同等で、srcディレクトリにあるCソースファイルの数を数えます。
commands引数はリストのリストです。
それぞれの内側のリストは
do-process
/run-process
が受け付けるcmd/args
の形式でなければなりません。少なくともひとつのコマンドが指定される必要があります。
指定されたコマンドは、最初のコマンドのstdoutが次のコマンドのstdinに、
そのコマンドのstdoutがさらに次のコマンドのstdinに、…という具合に
接続されてから、全てが並行して実行されます。
最初のコマンドのstdinはinputキーワード引数で指定された入力から供給され、
最後のコマンドのstdoutはoutputキーワード引数で指定された出力へと流れます。
これらのキーワード引数の省略時の値は、呼び出すプロセスのstdinとstdoutです。
これらキーワード引数に与えられる値については
do-process
/run-process
の項を見てください
(サブプロセスの実行)。
全てのプロセスのstderrはstderrキーワード引数で指定される出力へと流されます。 省略時は呼び出すプロセスのstderrが使われます。
do-process
と同じように、do-pipeline
は全てのプロセスの終了を待ち、
パイプライン末尾のプロセスが成功した(終了ステータスが0)場合に#t
を、
失敗した(終了ステータスが0以外)場合に#f
を返します。
ただし、on-abnormal-exit
キーワード引数に:error
を渡した場合は、
末尾のプロセスが失敗した場合にエラーが投げられます。
末尾以外のプロセスの終了ステータスはprocess-wait
によって回収されますが、
返り値には影響を与えず、
失敗した場合にon-abnormal-exit
引数が:error
であってもエラーは投げられません。
一方、run-pipeline
は末尾のプロセスの<process>
オブジェクトを返します。
パイプライン中の他のプロセスは末尾プロセスにprocess-upstreams
を適用すれば得られます。
デフォルトでは、run-pipeline
は全てのサブプロセスをバックグラウンドで起動して
直ちに戻ってきます。返り値の末尾プロセスに対してprocess-wait
を呼ぶと、
全てのサブプロセスが終了するまで待ちます。
waitキーワード引数に真の値を渡した場合は、
run-process
は全てのサブプロセスの終了を待ってから戻ります。
directoryとsigmaskキーワード引数は全てのプロセスに適用されます。
これらの引数の説明はdo-process
/run-process
の項を見てください
(サブプロセスの実行)。
註: Gauche 0.9.5で、run-process-pipeline
というAPIを導入しました。
これは現在のrun-pipeline
に似ていますが、起動したプロセスのリストを返します。
しかし使い勝手が悪いことがわかったので、run-process-pipeline
は非推奨とし、
run-pipeline
で置き換えることにしました。
run-process-pipeline
もしばらくサポートされますが、
できるだけ早くrun-pipeline
に移行してください。
Next: Process ports, Previous: プロセスパイプラインの実行, Up: gauche.process
- 高レベルプロセスインタフェース [Contents][Index]
{gauche.process}
子プロセスの状態を保持するためのオブジェクト。以下で説明される
run-process
手続きにより、プロセスを作ることができます。
次章で説明するプロセスポートもプロセスオブジェクトを用いています。
<process>
クラスは、run-process
やopen-input-process-port
といった高レベルAPIで作られた子プロセスの状態を管理しています。
それらの子プロセスの終了ステータスをとるには、
process-wait
やprocess-wait-any
といった
高レベルAPIを利用してください。これらの手続きはシステムコール以外の情報管理も
行います。sys-wait
やsys-waitpid
といった低レベルAPIで
直接子プロセスの終了ステータスを取ると、<process>
クラスの
内部状態に矛盾が生じます。
{gauche.process}
主にプロセスポートユーティリティ関数で使われるコンディション型。
<error>
を継承。このコンディション型は高レベルプロセスポートユーティ
リティが子プロセスが非ゼロのexitステータスで終了したことを検知したとき
に投げられます。
プロセスオブジェクト。
注: Unix用語では,exitステータスにかかわらず,プロセスがcalling
exit(2)
を呼ぶか,main()
から帰った場合を「正常な終了」と
しています。コマンドによっては非ゼロのexitステータスで何らかの正常な実行結果を
示すものもあります(grep(1)
など)。しかし,ほとんどのコマ
ンドでは,非ゼロの exit ステータスは要求された操作が実行できなかったこ
とを表わします。それゆえ上のような場合を例外的な場合として扱います。
{gauche.process}
≡ (is-a? obj <process>)
{gauche.process} サブプロセス process のプロセスIDを返します。
{gauche.process} サブプロセス process 内で起動されたコマンドを返します。
{gauche.process}
プロセスの入力もしくは出力に一方の端がつながれたパイプの、もう一方の端を取り出します。
nameはrun-process
のredirects引数に与えた
識別用の名前です。次の例を見てください。
(let1 p (run-process '(command arg) :redirects '((< 3 aux-in) (> 4 aux-out))) (let ([auxin (process-input p 'aux-in)] [auxout (process-output p 'aux-out)]) ;; feed something to the child's input (display 'something auxin) ;; read data from the child's output (read-line auxout) … ) (process-wait p))
シンボルaux-in
とaux-out
がパイプを識別するのに
使われています。process-input
が返すのは出力ポートであり、
process-output
が返すのは入力ポートであることに注意してください。
nameが省略された場合、process-input
はstdin
を、
process-output
はstdout
を使います。これらは
子プロセスの標準入力/出力をそれぞれ:input :pipe
/:output :pipe
で
リダイレクトした場合に使われる名前です。
名前に対応するパイプが無い場合は#f
が返ります。
(let* ((process (run-process '("date") :output :pipe)) (line (read-line (process-output process)))) (process-wait process) line) ⇒ "Fri Jun 22 22:22:22 HST 2001"
processがrun-pipeline
の結果であった場合、
(process-input process)
と(process-input process 'stdin)
は
少し違う振る舞いをします。
process自体はプロセスパイプラインの末尾のプロセスを表していますが、
上記二つの呼び出しに限り、
プロセスパイプラインの先頭のプロセスのstdinにつながれたパイプを返します。
これによって、パイプライン全体をひとまとめに扱うことができます。
(let1 p (run-pipeline `((cat) (grep "aba")) :input :pipe :output :pipe) (display "banana\nhabana\ntabata\ncabara\n" (process-input p)) ; head of the pipeline (close-port (process-input p)) (process-wait p) (port->string (process-output p))) ⇒ "habana\ntabata\ncabara\n"
もしdo-pipeline
/run-pipeline
を使わずに
一つのプロセスの出力を別のプロセスの入力につなげたい場合、
何らかのコルーチンかスレッドで一つのプロセスの出力をアクティブに読んで
もう一つのプロセスの入力へと送ることが必要になります。
control.plumbing
を使えばそれは簡単にできます
(control.plumbing
- ポートの配管参照)。また、流れるデータをモニタするといったことも可能です。
{gauche.process}
これは(process-output process 'stderr)
と等価です。
{gauche.process}
process が生きている場合は真を返します。process-wait
によって
明示的にチェックされない限り、Gauche はサブプロセスのステータスを知ることが
できないことに注意してください。
{gauche.process}
processがrun-pipeline
の結果であった場合、
パイプライン中でprocessの上流にあるプロセスのリストが返ります。
processがrun-pipeline
の結果でなかった場合は空リストが返ります。
(define p (run-pipeline `((cat) (grep "ho") (wc)) :input :pipe)) p ⇒ #<process 20658 "wc" active> (process-upstreams p) ⇒ (#<process 20656 "cat" active> #<process 20657 "grep" active>)
{gauche.process}
アクティブなプロセスのリストを返します。プロセスは、その終了ステータスが
process-wait
によって明示的に回収されない場合は、アクティブなまま
残ります。
ひとたび終了ステータスが回収され、プロセスの状態がインアクティブに
変更されると、そのプロセスはprocess-list
が返すリストからは除かれます。
{gauche.process}
サブプロセス process の終了ステータスを取得し、process
のstatusスロットに値を格納します。statusスロットの値は
process-exit-status
で得ることができます。
デフォルトでは、この手続きはprocess が終了するまで実行を一時停止します。 しかし、nohangに真の値が与えられた場合は、processが終了して いない場合にも直ちに返ります。
オプショナル引数error-on-nonzero-statusに真の値が与えられた場合、
この手続きは得られた終了ステータスが0で無い場合に
<process-abnormal-exit>
エラーを投げます。
この呼び出しによってprocessの終了ステータスが実際に取得された場合は
#t
を、そうでなければ#f
を返します。
プロセスオブジェクトがrun-pipeline
によって作られたものである場合、
process-wait
は、nohangに真の値が与えられなければ、
パイプラインに関わる全てのサブプロセスの終了を待ちます。
但し、error-on-nonzero-statusはprocess引数、すなわちパイプラインの
最後のプロセスにしか効力を持ちません。その他のサブプロセスがステータス0以外で終了
したとしても、その事実は各サブプロセスのプロセスオブジェクトに記録されるだけで、
エラーは通知されません。
パイプラインのプロセスに対してnohangに真の値を指定した場合、
process-wait
はパイプラインの上流のプロセスに対しても終了のチェックを
行い、終了していた場合は対応するプロセスオブジェクトの終了ステータスを更新しますが、
終了していないサブプロセスについてはそのままにします。
それらのサブプロセスの終了ステータスを回収するには、それぞれのプロセスオブジェクト
についてprocess-wait
を発行するか、下のprocess-wait-any
を使います。
{gauche.process}
run-process
で作られたサブプロセスのどれかの終了ステータスを取得します。
終了ステータスが取得できたプロセスのプロセスオブジェクトを返します。
真の値がnohangに与えられた場合は、どの子プロセスも終了していない場合は
直ちに#f
を返します。そうでなければ、この手続きはいずれかの子プロセスが
終了するまで待ちます。
子プロセスが存在しない場合は、この手続きは直ちに#f
を返します。
{gauche.process} processの終了ステータスを、intervalナノ秒間隔でポーリングします (デフォルトは2e6ns=2msです)。processが終了していることを見つけたら 終了ステータスを回収して直ちに戻ります。
max-waitが指定されて#f
でなければ、それはポーリングを続ける期間の
最大値をナノ秒単位で指定します。その期間が過ぎたら、この手続きは諦めて戻ります。
指定されないか#f
の場合は、プロセスが終了するまでポーリングしつづけます。
continue-testが与えられて#f
でなければ、
それは1引数の手続きでなければなりません。
ポーリングでプロセスが終了してなかったら、その度にこの手続きが呼ばれます。
引数はポーリングした回数で、0から始まりポーリングの度に1つづつ増えてゆきます。
この手続きが#f
を返したら、その時点でポーリングは打ちきられ
process-wait/poll
は直ちに戻ります。
真の値を返したらポーリングは継続されます。
raise-error?はprocess-wait
と同じで、
processの終了ステータスが0でなければエラーを投げます。
戻り値は、プロセスが終了してステータスを回収できれば#t
、
途中で諦めた場合は#f
となります。終了ステータス自体は
processからprocess-exit-status
で読み出してください。
{gauche.process}
process-wait
によって取得されたprocessの終了ステータスを
返します。processに対してprocess-wait
を呼ぶ前にこの手続きを
呼んだ場合の結果は未定義です。
終了ステータスの解釈はプラットフォームに依存します。プロセスが自発的に
(exit
を呼んで)終了したか、それともシグナルによって終了させられたかを
確かめるにはsys-wait-exited?
かsys-wait-signaled?
を
使ってください。また、終了コードもしくは終了と原因となったシグナルを
知るにはsys-wait-exit-status
をsys-wait-termsig
使ってください (プロセス管理参照)。
{gauche.process} サブプロセス process にシグナル signal を送ります。 signal は正確整数のシグナルナンバーでなければなりません。 シグナルの定義済み変数については、シグナルを参照して下さい。
{gauche.process} それぞれ、process に、SIGKILL、SIGSTOP、SIGCONT を送ります。
processを、徐々に強力な方法を使って終了させようとします。
ask引数は、#f
でなければ、試行回数を引数にとる手続きでなければなりません。
ask手続きはprocessに何らかの方法で作用して終了を試みるものです
(例えば、通信チャネルからexit
コマンドを送る等)。
askを呼び出し後(戻り値は捨てられます)、プロセスの状態が検査され、
プロセスがexitしたら直ちにprocess-shutdown
は#t
を返します。
プロセスがすぐにexitしなければ、ask-intervalナノ秒おきにask手続きが
最大retry回まで呼び出されます。askに渡される引数は呼び出しの度に
インクリメントされます。
ask引数に#f
が渡された場合、このステップは省かれます。
省略時のデフォルト値は、askが#f
、ask-intervalが50e6 ns (50ms)、
ask-retryが1です。
最初のステップでプロセスが止まらなければ、次にシグナルを送り始めます。
シグナルが送られるたびにプロセスの状態が検査され、終了していれば直ちに
process-shutdown
は#t
を返します。
どのシグナルが送られるかはsignals引数で決まり、
またシグナルを送る間隔はsignal-intervalで指定されます。
signalsのデフォルト値は(list SIGTERM SIGTERM SIGKILL)
、
signal-intervalのデフォルト値は50e6 ns (50ms)です。
全てを試みてもまだプロセスが終了していなければ、#f
が返されます。
(ヒント: プロセスが終了するまでシグナルを送りつづけたければ、 signalsに循環リストを渡すことで実現できます)
Next: Process connection, Previous: Process object, Up: gauche.process
- 高レベルプロセスインタフェース [Contents][Index]
{gauche.process} command を子プロセスで非同期に実行します。 走らせた子プロセスの標準出力につながれた入力ポートと、 プロセスオブジェクトの二つの値を返します。
commandは文字列か、コマンド名と引数のリストか、「コマンド名と引数のリスト」のリストです。
文字列の場合、それは/bin/sh
に渡されます。
環境変数の置換やグロブパターン、リダイレクトなどのシェルの機能が
文字列中で使えます。
文字列をつなぎ合わせてコマンドラインを作成する場合、
特殊文字をシェルに解釈してほしくなければ、それを正しくエスケープするのは
呼び出し元の責任です。下で説明するshell-escape-string
は
助けになるかもしれません。
commandがリスト(ただしリストのリストではない)の場合は、
各要素がx->string
で文字列に
変換された後に、sys-exec
を使って直接コマンドを起動します
(リストのcar
がコマンドのパス名とargv[0]
の両方に使われます)。
シェルの介入を避けたい場合はこの形式を使うと良いでしょう。
特殊文字をエスケープする必要はありません。
デフォルトでは、子プロセスの標準入力は/dev/null
にリダイレクトされ、
標準エラー出力は呼び出したプロセスと共有されます。
inputとerrorキーワード引数にパス名を与えることで、
これらの出力をリダイレクトすることができます。
commandがリストのリストの場合は、コマンドパイプラインが作られます
(run-pipeline
, プロセスパイプラインの実行参照)。
内側のリストは、コマンドパスとそのコマンドへの引数で、x->string
で文字列に
変換された後、sys-exec
に渡されます。
最後のコマンドの標準出力を、返されるポートから読み出すことができます。
最初のコマンドの標準入力は、inputキーワード引数にパス名が渡されれば
そのファイルから、そうでなければ/dev/null
から供給されます。
各コマンドの標準エラー出力は、errorキーワード引数にパス名が渡されればそのファイルに、
そうでなければ呼び出したプロセスの標準エラー出力に流れます。
また、プロセスの出力の文字エンコーディングを指定するために
encodingキーワード引数を与えることもできます。
それがutf-8
でなければ、
open-input-process-port
は文字コード変換ポートを挿入します。
encodingが与えられた場合、conversion-buffer-sizeキーワード引数で
変換バッファの大きさを指定することも可能です。文字コード変換の詳細については
gauche.charconv
- 文字コード変換を参照して下さい。
(receive (port process) (open-input-process-port "ls -l Makefile") (begin0 (read-line port) (process-wait process))) ⇒ "-rw-r--r-- 1 shiro users 1013 Jun 22 21:09 Makefile" (receive (port process) (open-input-process-port '(ls -l "Makefile")) (begin0 (read-line port) (process-wait process))) ⇒ "-rw-r--r-- 1 shiro users 1013 Jun 22 21:09 Makefile" (open-input-process-port "command 2>&1") ⇒ ;the port reads both stdout and stderr (open-input-process-port "command 2>&1 1>/dev/null") ⇒ ;the port reads stderr
サブプロセスの終了ステータスは自動的に回収されません。
process-wait
を呼ぶことは呼び出し側の責任であり、これを怠ると
サブプロセスはゾンビプロセスになります。それが面倒であれば、以下の
手続きを使うことができます。
{gauche.process} 子プロセスでcommand を実行し、その標準出力と入力ポートを パイプで繋ぎ、そのポートを引数として proc を呼び出します。 proc が返るとその終了ステータスを回収し、proc が返した 結果を返します。proc がエラーを通知しても、クリーンアップは 行われます。
キーワード引数on-abnormal-exitは子プロセスが0以外の終了ステータス
を返した場合の振舞いを指定します。その値は:error
(デフォルト)、
:ignore
、もしくは一引数の手続きでなければなりません。
値が:error
の場合、0以外の終了ステータスは
<process-abnormal-exit>
エラーコンディションを発生させます。
コンディションオブジェクトのprocess
スロットには子プロセスオブジェクトが
保持されます。値が:ignore
の場合、0以外の終了ステータスに対して
特別なアクションは取られません。値が手続きの場合、0以外の終了ステータスに対して
子プロセスオブジェクトを引数にしてその手続きが呼ばれます。その手続きが
戻れば、call-with-input-process
は正常動作と同じように戻ります。
on-abnormal-exitの意味がdo-process
とは異なることに注意してください
(サブプロセスの実行参照)。この手続きを使う場合は、
単にサブプロセスの成功/失敗を知るだけでなく、より細かな制御を必要とする場合が多いからです。
commandおよび他のキーワード引数の意味はopen-input-process-port
と
同じです。
(call-with-input-process "ls -l *" (lambda (p) (read-line p)))
{gauche.process} 子プロセスで command を実行し、コマンドの標準出力に 接続された現在の入力ポートとともに thunk を呼び出します。 thunkが終了するかエラーを投げた後に、コマンドの終了ステータスが 回収されます。
commandおよびキーワード引数の意味はcall-with-input-process
と
同じです。
(with-input-from-process "ls -l *" read-line)
{gauche.process}
子プロセスで command
を非同期に実行します。
子プロセスの標準入力に接続された出力ポートと、
プロセスオブジェクトの二つの値を返します。
command引数、およびencodingとconversion-buffer-sizeの
意味は、open-input-process-port
と同じです。
デフォルトでは、子プロセスの標準出力は/dev/null
にリダイレクトされ、
標準エラー出力は呼び出したプロセスと共有されます。
outputとerrorキーワード引数にパス名を与えることで、
これらの出力をリダイレクトすることができます。
サブプロセスの終了ステータスは自動的には回収されません。
適切なタイミングで、サブプロセスに対して process-wait
を呼ぶ
必要があります。
{gauche.process}
command
を子プロセスで実行し、コマンドの標準入力に
接続された出力ポートとともに proc を呼び出します。
コマンドの終了ステータスは、proc が返るかエラーを通知した
後に回収されます。
キーワード引数の意味はopen-output-process-portと同じです。
ただしon-abnormal-exitについてはcall-with-input-process
で説明したのと同じ意味です。
(call-with-output-process "/usr/sbin/sendmail" (lambda (out) (display mail-body out)))
{gauche.process}
コマンドの標準入力に接続された出力ポートが、thunk の実行中は
現在の出力ポートにセットされることを除いて、call-with-output-process
と同じです。
{gauche.process} command をサブプロセスで実行し、proc を2つの引数と ともに呼び出します。最初の引数は入力ポートで、コマンドの標準出力に 接続されたものです。2番目の引数は出力ポートでコマンドの標準入力に 接続されたものです。コマンドからのエラー出力は、errorキーワード 引数でパス名が指定されない限り、呼び出したプロセスのエラー出力が共有されます。
コマンドの終了ステータスは、procが戻るかエラーを投げた場合に 回収されます。
{gauche.process}
command を実行し、その(標準出力への)出力を回収して返します。
process-output->string
は command からの全ての出力を連結し
1つの文字列とします。その際、空白文字からなるシーケンスは1つの空白に
置換されます。このアクションは、シェルスクリプトにおける「コマンド置換」
に似たものです。
process-output->string-list
は command からの出力を行ごとに
回収し、それらをリストにしたものを返します。改行文字は削除されます。
内部的には、command は call-with-input-process
により
実行されます。キーワード引数はcall-with-input-process
に
そのまま渡されます。
(ヒント: 子プロセスのstderr出力も一緒に結果として得たい場合は、
:error
引数に:merge
を渡します。
詳しくは上のrun-process
の項を見てください。)
(process-output->string '(uname -smp)) ⇒ "Linux i686 unknown" (process-output->string '(ls)) ⇒ "a.out foo.c foo.c~ foo.o" (process-output->string-list '(ls)) ⇒ ("a.out" "foo.c" "foo.c~" "foo.o")
{gauche.process} strがシェルのコマンドライン引数解析に影響を与える文字を含んでいる場合、 それらがシェルによって解釈されないようにエスケープされた文字列を返します。 そうでなければstr自体を返します。
省略可能なflavor引数はシンボルを取り、プラットフォームを指定します。
現在はwindows
かposix
が指定可能です。シェルが
エスケープやクオートを処理する方法がこの二つのプラットフォームで大きく
異なるからです。windows
フレーバーの場合はMVCSランタイムの
引数パージングに合わせ、posix
フレーバーの場合はIEEE Std 1003.1に
合わせます。
省略された場合は、プロセスが走っているプラットフォームが
デフォルトの値となります。(Cygwinはposix
とみなされます。)
自分でコマンドライン文字列を組み立てる必要がある場合に使ってください。 (単一のコマンドライン文字列でなく、コマンドライン引数のリストを渡す場合は エスケープの必要はありません。引数はシェルを通さずに子プロセスに渡される からです。)
{gauche.process} シェルがやるように、文字列strを引数リストに分割します。
(shell-tokenize-string "grep -n -e \"foo bar\" log") ⇒ ("grep" "-n" "-e" "foo bar" "log")
省略可能なflavor引数はシンボルwindows
かposix
を取り、
構文を指定します。windows
の場合はMVCSランタイムの
引数パージングに合わせ、posix
フレーバーの場合はIEEE Std 1003.1
Shell Command Languageに合わせます。
省略された場合は、プロセスが走っているプラットフォームが
デフォルトの値となります。(Cygwinはposix
とみなされます。)
この手続きはシェルの変数置換のような高度な機能は持っていません。 もしそういった解釈が必要なメタ文字に出会った場合はエラーが投げられます。 言い換えれば、メタ文字はstrの中で全て適切にクオートされている 必要があります。
(shell-tokenize-string "echo $foo" 'posix) ⇒ signals error (shell-tokenize-string "echo \"$foo\"" 'posix) ⇒ still signals error (shell-tokenize-string "echo '$foo'" 'posix) ⇒ ("echo" "$foo") (shell-tokenize-string "echo \\$foo" 'posix) ⇒ ("echo" "$foo")
Previous: Process ports, Up: gauche.process
- 高レベルプロセスインタフェース [Contents][Index]
外部プロセスとの通信を<connection>
として見せるクラスです。
コネクションインタフェースについてはgauche.connection
- コネクションフレームワークを参照してください。
これを使うとコネクションを期待しているコードに外部プロセスを渡せます。 例えばネットワークと通信する部分を、直接の通信のかわりにリモートサーバの間に フィルタプロセスを入れる、といったことが簡単にできます。
外部プロセスを起動し、その標準入出力とコネクションとして通信する コネクションオブジェクトを返します。
process-or-specにはコマンドと引数からなるリストか、
<process>
オブジェクトを渡すことができます。
<process>
オブジェクトならそのプロセスの標準入力と標準出力はパイプに
なっていなければなりません。
リストの場合は、それがrun-process
に渡されて新たなプロセスが起動されます。
コネクションの入出力チャネルを両方ともシャットダウンすれば、プロセスは終了します。 多くのプロセスは、標準入力からEOFを読むと終了するので、 まずは標準入力へのチャネルをシャットダウンしてから少し待ちます。 もしそれでプロセスが終了する気配を見せなかった場合、シグナルSIGTERMを送り、 それでも終了しなかったらSIGKILLを送ります。
コネクションをクローズするだけではプロセスは終了しません。 forkしたプロセスがまだ通信するかもしれないからです。
Next: gauche.record
- レコード型, Previous: gauche.partcont
- 部分継続, Up: ライブラリモジュール - Gauche拡張モジュール [Contents][Index]