Using Ruby 1.8.7, can I execute shell and capture PID, STDOUT, STDERR, status?

955 Views Asked by At

I am trying to run a shell script and capture the PID, STDERR, STDOUT and the exit status of the shell.

I am using Ruby 1.8.7, so Open3 doesn't have a way for getting the PID. I tried using the open4 gem, but unfortunately a few scripts hung while in the write process, which runs fine when manually run.

I would like to find an alternative. Your guidance will be much appreciated!

1

There are 1 best solutions below

4
David Ljung Madison Stellar On

Unfortunately there isn't an easy way to do this. I tried PTY.spawn but sometimes it would fail to exec. If you can't use open3, then you can use FIFOs, but it gets a bit messy. Here's a solution I used on 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

Nope, it's not clean. But because it uses fork and deals with the three handles itself, then you can get the PID and accomplish what open3 does.

Make sure to close your filehandles after! A yield version of this that cleans up afterwards would probably make more sense.