使用Ruby 1.8.7,我可以执行shell并捕获PID,STDOUT,STDERR,状态吗?

时间:2013-08-30 21:28:16

标签: ruby shell open4

我正在尝试运行shell脚本并捕获PID,STDERR,STDOUT和shell的退出状态。

我使用的是Ruby 1.8.7,因此Open3无法获取PID。我尝试使用open4 gem,但遗憾的是在写入过程中挂起了一些脚本,手动运行时运行正常。

我想找个替代方案。非常感谢您的指导!

1 个答案:

答案 0 :(得分:0)

不幸的是,没有一种简单的方法可以做到这一点。我试过PTY.spawn,但有时它会失败。如果你不能使用open3,那么你可以使用FIFO,但它有点乱。这是我在1.8.7上使用的解决方案:

# Avoid each thread having copies of all the other FDs on the fork
def closeOtherFDs
  ObjectSpace.each_object(IO) do |io|
    unless [STDIN, STDOUT, STDERR].include?(io)
      unless(io.closed?)
        begin
          io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
        rescue ::Exception => err
        end
      end
    end
  end
end


# Utilities for fifo method
require 'tempfile'
def tmpfifo
  # Use tempfile just to manage naming and cleanup of the file
  fifo = Tempfile.new('fifo.').path
  File.delete(fifo)
  system("/usr/bin/mkfifo",fifo)
  #puts "GOT: #{fifo} -> #{$?}"
  fifo
end

# fifo homebrew method
def spawnCommand *command
  ipath = tmpfifo
  opath = tmpfifo
  #epath = tmpfifo

  pid = fork do
    #$stdin.reopen(IO.open(IO::sysopen(ipath,Fcntl::O_RDONLY)))
    $stdin.reopen(File.open(ipath,'r'))
    $stdout.reopen(File.open(opath,'w'))
    #$stderr.reopen(File.open(epath,'w'))
    $stderr.close
    closeOtherFDs
    exec(*command)
    exit
  end

  i = open ipath, 'w'
  #i.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) # Doesn't work?  Use closeOtherFDs
  o = open opath, 'r'
  #e = open epath, 'r'
  e = nil
  [o,i,e].each { |p| p.sync = true if p }

  [o,i,e]
end

不,这不干净。但是因为它使用fork并处理三个句柄本身,所以你可以获得PID并完成open3所做的事情。

请务必在关闭文件句柄后关闭!之后清理的收益版本可能更有意义。