LoginSignup
11
10

More than 5 years have passed since last update.

rubyでちょっと便利な外部コマンド実行

Last updated at Posted at 2015-08-15

rubyで外部コマンドを実行する方法はいくつかありますが、ライブラリなしだと標準エラーが取れなかったりと不便ですし、ライブラリを入れると小回りがきかないです。

そこで自分で作ってみました。よかったら使ってください。

ソース

require 'fileutils'
require 'tempfile'
require 'tmpdir'
require 'timeout'

class Cmd
  @@timeout = 0
  def Cmd.timeout=(sec) ; @@timeout = sec ; end
  def Cmd.run(*cmd_array)
    raise(Exception,"Command string is nil") if cmd_array.size == 0
    stdout_file = Tempfile::new("#{$$}_light_cmd_tmpout",Dir.tmpdir)
    stderr_file = Tempfile::new("#{$$}_light_cmd_tmperr",Dir.tmpdir)
    pid = spawn(*cmd_array, :out => [stdout_file,"w"],:err => [stderr_file,"w"])
    status = nil
    if @@timeout != 0
      begin
        timeout(@@timeout) do
          status = Process.waitpid2(pid)[1] >> 8
        end
      rescue Timeout::Error => e
        begin
          Process.kill('SIGINT', pid)
          raise(Exception,"Timeout #{@@timeout.to_s} sec. Kill process : PID=#{pid}" )
        rescue
          raise(Exception,"Timeout #{@@timeout.to_s} sec. Fail to kill process : PID=#{pid}" )
        end
      end
    else
      status = Process.waitpid2(pid)[1] >> 8
    end
    return  Result.new(stdout_file,stderr_file,status,cmd_array)
  end

  class Result
    attr_reader :stdout_file, :stderr_file, :status, :cmd_array
    def initialize(stdout_file, stderr_file, status, cmd_array)
      @stdout_file = stdout_file
      @stderr_file = stderr_file
      @status = status
      @cmd_array = cmd_array
    end
    def stdout  ; File.read(@stdout_file) ; end
    def stderr  ; File.read(@stderr_file) ; end
    def to_h    ; {:cmd_str => cmd_str,:stdout => stdout, :stderr => stderr, :status => status} ; end
    def cmd_str ; @cmd_array.join(" ") ; end
    def to_s    ;to_h.to_s ; end
  end
  class Exception < StandardError ; end
end

使い方

Cmd.runにコマンドを渡すだけです。

ret = Cmd.run("uname -r")
p ret.stdout
=> "2.6.32-220.el6.x86_64\n"
p ret.stderr
=> ""
p ret.status
=> 0

タイムアウトも仕込めます

Cmd.timeout = 1 
Cmd.run("sleep 2");
=> 例外発生してくれます

長い標準出力でも、一時ファイルに吐かれているの、後から少しずつ読めます。

ret = Cmd.run("rpm -qa")
while line = ret.stdout_file.gets
  puts line
  sleep 1
end
=> 1行づつ出力
11
10
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
10