動かざることバグの如し

近づきたいよ 君の理想に

Rubyで外部コマンドを実行するならopen3が1番よさそう

環境

やりたいこと

Rubyでは様々な方法でコマンドを実行できるが、1番シンプルなのはバッククオートを使う方法だと思う

p `date` # dateコマンドを実行

これは本当に楽なのだが、欠点が一つあって標準出力は取れるが、標準エラー出力、さらに結果のstatusが取れない。したがってそのコマンドが正常に終了したかどうかはバッククオートのやり方ではわからない

そこでsystemuのライブラリ入れたりなんやかんややってたが、実は標準ライブラリで実現できた

やりかた

open3という標準ライブラリを使う。

require 'open3'
cmd = "mkdir /tmp/hoge"
stdout, stderr, status = Open3.capture3(cmd)
p stdout
p stderr
p status

これで

が入る

以下は正常に終了した例

""
""
#<Process::Status: pid 30477 exit 0>

以下は失敗した例

""
"mkdir: /tmp/hoge: File exists\n"
#<Process::Status: pid 30540 exit 1>

蛇足

ちなみにruby2.6以降だが、system()で外部コマンドを実行する際にexception:trueを渡すとプロセス終了ステータスが異常だった場合に例外が投げられる

cmd = "mkdir /tmp/hoge"
system(cmd, exception: true)

結果

$ruby a.rb 
mkdir: /tmp/hoge: File exists
Traceback (most recent call last):
        1: from a.rb:6:in `<main>'
a.rb:6:in `system': Command failed with exit 1: mkdir (RuntimeError)

もちろんcatchすることも可能。が、標準エラー出力を取得することはできないっぽい?

cmd = "mkdir /tmp/hoge"
begin
  system(cmd, exception: true)
rescue => e
  puts e.class
  puts e.message
end

結果

$ ruby a.rb 
mkdir: /tmp/hoge: File exists
RuntimeError
Command failed with exit 1: mkdir