外部のコマンドを実行

はじめに

Ruby から外部のコマンドを実行する場合、使えるメソッドは以下のとおりです。

  • exec
  • system
  • ``
  • IO.popen

これらそれぞれの使い方と、実行結果を解説します。

exec

exec はコマンドの失敗時にエラーを返す他は何ら値を返しません。irb で実行するとこんな感じ。

irb(main):001:0> exec("ruby -v")
ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32]

そして irb は終了してしまいました。

system

system ではコマンドが成功すると true を、失敗すると false を返します。

irb(main):001:0> system("ruby -v")
ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32]
=> true
irb(main):002:0> system("open_sesami")
=> false

こちらは、外部の PHP モジュールと連携した例。

if system("php sample.php")
  p "正常終了です"
else
  p "異常終了です"
end

外部に処理を投げる時、処理結果を真偽値で受け取ればいいケースなどはこれで。

``

`` を利用すると外部から真偽値以外の値を直接受け取る事ができます。コマンドの失敗時には例外が発生し、値は返りません(nil になります)。

irb(main):001:0> a = `ruby -v`
=> "ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32]\n"
irb(main):002:0> p a
"ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32]\n"
=> nil

http://d.hatena.ne.jp/spitfire_tree/20080809/1218266645
外部の JAVA モジュールに引数を渡して、結果を受け取ったサンプル。

def download
  item = Item.find(params[:id])

  if item
    # 引数を渡しつつ java のモジュールから値を受け取る
    data = `java MyImageCreator #{item.value}`

    if data
      send_data(data, :type => "application/something")
    end
  end
end

実際の利用時にはエラー処理が必須でしょう。
なお、値を渡す側では標準出力に出力を行うように設定する必要があります。

IO.popen

これは、標準入出力を使って外部とやりとりをします。

cmd  = "java test.java" # 実行するコマンド
cin  = "This is a dog." #標準入力に流すテキスト

IO.popen(cmd, 'r+') do |pipe|
  pipe.write(cin)
  pipe.close_write
  p pipe.read
end

バイナリデータを使うならこちら。PDF を受け取ってみたり。

cmd = "java iText.StreamToRubyMethodTest 1"

IO.popen(cmd, 'r+') do |pipe|
  pipe.binmode
  byte_stream = pipe.read
  File.open("unk.pdf", 'wb') do |output|
    output.binmode
    output.write(byte_stream)
    output.close
  end
end