Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redirecting stdout/stderr of spawn() to a string in Ruby

Tags:

ruby

I would like to execute an external process in Ruby using spawn (for multiple concurrent child processes) and collect the stdout or stderr into a string, in a similar way to what can be done with Python's subprocess Popen.communicate().

I tried redirecting :out/:err to a new StringIO object, but that generates an ArgumentError, and temporarily redefining $stdxxx would mix up the outputs of the child processes.

like image 467
Dologan Avatar asked May 02 '12 12:05

Dologan


4 Answers

In case you don't like popen, here's my way:

r, w = IO.pipe
pid = Process.spawn(command, :out => w, :err => [:child, :out]) 
w.close

...

pid, status = Process.wait2
output = r.read
r.close

Anyway you can't redirect to a String object directly. You can at most direct it to an IO object and then read from that, just like the code above.

like image 67
Robert Avatar answered Nov 15 '22 01:11

Robert


Why do you need spawn? Unless you are on Windows you can use popen*, e.g. popen4:

require "open4"

pid, p_i, p_o, p_e = Open4.popen4("ls")
p_i.close
o, e = [p_o, p_e].map { |p| begin p.read ensure p.close end }
s = Process::waitpid2(pid).last
like image 25
Victor Moroz Avatar answered Nov 15 '22 01:11

Victor Moroz


From the Ruby docs it seems that you can't, but you can do this:

spawn("ls", 0 => ["/tmp/ruby_stdout_temp", "w"])
stdoutStr=File.read("/tmp/ruby_stdout_temp")

You can also do the same with standard error. Or, if you wan't to do that and don't mind popen:

io=IO.popen("ls")
stdout=io.read
like image 31
Linuxios Avatar answered Nov 15 '22 01:11

Linuxios


The most simple and straightforward way seems

require 'open3'

out, err, ps = Open3.capture3("ls")

puts "Process failed with status #{ps.exitstatus}" unless ps.success?

Here we have the outputs as strings.

like image 27
TNT Avatar answered Nov 15 '22 01:11

TNT