『Working with Unix Processes』の Spawning Terminal Processes を読みました
はじめに
『Daemon Processes』の続きです。
勉強会でのまとめ
本文の自分の理解のまとめ、本文の知識をもって試したこと、議論したこと、自分の理解のための補足などを書きます。
イントロダクション
Rubyプログラムからシェルのコマンドを実行する方法には以下のものがある。
exec
Kernel#system
- ブロッキング。後続の処理は実行される
- 戻り値はコマンド実行後の exit code が 0 なら true で 0以外なら false
- コマンドの実行結果はターミナルに表示される
Kernel#` もしくは %x[]
- ブロッキング。後続の処理は実行される
- 戻り値には結果が文字列として返る
- コマンドの結果をプログラムで処理したい場合に使えそう
Process.spawn
- ノンブロッキング。後続の処理は実行される
- あえてブロックにしたければ戻り値の pid を wait すればいい
- オプションで STDERR => STDOUT などとリダイレクトできる
Rubyプロセスを wait することは今までやってきたが、spawn した"ターミナルプロセス"も wait できる。Unix の柔軟性!
蛇足ですが、コマンド実行時の標準出力・標準エラーのリダイレクトの書き方ををよくわすれるので書いておきます。
$ ls --help 1>>suc.log 2>>err.log # 追記 $ ls --help 1>suc.log 2>err.log # 上書き
これに関してはこちらの記事『UNIXの部屋 コマンド検索:リダイレクト (*BSD/Linux)』がとても詳しいです。
IO.popen
- ブロッキング。popenブロックを書かないとコマンドが実行されないようだ
- popenブロック内でそのパイプのストリームにアクセスできる
Open3.popen3
STDIN, STDOUT, STDERR すべてにアクセスできる。
以下、本文中のサンプルコードを試してうまくいかなかった。`:err => :out` の部分の仕様が変わったのかな。。
# 実行した環境では `ls --help` するとそのようなオプションは無いとエラーが起きる # オプションで標準エラーを標準出力にリダイレクトして表示されることを期待している Open3.popen3('ls', '--help', :err => :out) {|stdin, stdout, stderr| puts stdout.read # なぜか表示されなかった puts stderr.read # こっちは表示された }
調べていたら popen4 というのも見つけた。
まとめ
これらのメソッドは fork を用いて実現されている。そのためメモリを丸々コピーする fork のコストは考慮すること。Ruby で fork のコストをかけずに shell out する方法はコアライブラリーではサポートしていない。posix-spawn プロジェクトでは低コストで shell out する方法をサポートしている。
https://github.com/rtomayko/posix-spawn
なぜ posix_spawn が早いのかというと、ファイルディスクリプタのコピーだけしか保持しないため。fork はそれに加えてメモリもコピーするのでその分重い。
フレーズ
- Voila!: ほら見てごらん!
- underneath: 裏では
- drawback: 不都合
- incur: (良くないものを)こうむる
- discerning attributes: シャープな(聡明な)引数(カッコイイ引数。引数のことを褒めてる表現だと思う)
- shell out: シェルアウト。Unix の用語みたい。プログラム内でコマンドを実行すること(だと思う)