52

Is there some way to run a (shell) command from Ruby displaying but also capturing the output? Maybe with the help of some gem?

What I mean by displaying is not printing it at the end, but as it appears, so the user gets the feedback of what's going on when running slow commands.

Pablo Fernandez
  • 279,434
  • 135
  • 377
  • 622

5 Answers5

81

You can run system call like this:

`sleep --help`

Or like this

system "sleep --help"

Or

%x{ sleep --help }

In case of system it will print output and return true or nil, other two methods will return output

PS Oh. It is about displaying in real time.

So. You could use something like this:

system("ruby", "-e 100.times{|i| p i; sleep 1}", out: $stdout, err: :out)

To print data in realtime and store it in variable:

output = []
r, io = IO.pipe
fork do
  system("ruby", "-e 3.times{|i| p i; sleep 1}", out: io, err: :out)
end
io.close
r.each_line{|l| puts l; output << l.chomp}
#=> 0
#=> 1
#=> 2
p output
#=> ['0', '1', '2']

Or use popen

output = []
IO.popen("ruby -e '3.times{|i| p i; sleep 1}'").each do |line|
  p line.chomp
  output << line.chomp
end
#=> '0'
#=> '1'
#=> '2'
p output
#=> ['0', '1', '2']
fl00r
  • 82,987
  • 33
  • 217
  • 237
  • 1
    I know those ways of running a command, none of them capture the output into a string and output it at the same time. – Pablo Fernandez Apr 19 '12 at 09:21
  • I don't see any variant of `system` that takes `out` and `err` parameters? Is this Ruby 2.0? – mxk Nov 09 '13 at 17:34
  • @Matthias for old ruby you need => ("hash rocket") notation, so: , out: io, err: :out becomes , :out => io, :err => :out – Simon B. Oct 29 '14 at 15:02
  • system("ruby", "-e 3.times{|i| p i; sleep 1}", :out => io, :err => :out) NOT WORKING: in `system': can't convert Hash into String (TypeError) – user180574 Oct 14 '15 at 20:53
  • system with out: and err:, nice! – Rafael Oliveira Oct 24 '15 at 01:34
  • I like the last answer - I did not have the idea of using IO.popen() and .each on it. system() does line-output, whereas default `` backtick waits until it has finished. – shevy Aug 03 '17 at 00:18
  • As far as I can tell, `system` does not actually take `out` and `err` arguments like that, in any version of ruby I can find. I'm not sure where that came from. It would be nice! – jrochkind Dec 06 '22 at 21:06
17

I used open3 to captured the output of executed shell command from ruby code.

require 'open3'

stdout, stdeerr, status = Open3.capture3("ls")

puts stdout
Kannan S
  • 2,459
  • 1
  • 17
  • 17
16

You can redirect the output

system 'uptime > results.log'

or save the results.

result = `uptime`
result = %x[uptime]

see here. Getting progress information or output in realtime is more complicated, I doubt that there is a simple solution. Maybe it is possible with advanced process management functions such as Open3.popen3. You could also try to use a pseudo terminal with pty and grap the output there.

Community
  • 1
  • 1
0x4a6f4672
  • 27,297
  • 17
  • 103
  • 140
0

If you are willing to explore a solution outside the standard library, you may also use Mixlib::ShellOut to both stream output and capture it:

require 'mixlib/shellout'
cmd = 'while true; do date; sleep 2; done'
so = Mixlib::ShellOut.new(cmd)
so.live_stream = $stdout
so.run_command
out = so.stdout
jayhendren
  • 4,286
  • 2
  • 35
  • 59
0

Have a look at the Ruby docs for the non-blocking form of Open3.popen3:

https://ruby-doc.org/stdlib-3.1.2/libdoc/open3/rdoc/Open3.html#method-c-popen3

Although the name "popen3" is not as familiar as the "system()" command, it is currently the recommended method for running system commands from Ruby with several advantages over the system() command. There are several variations in the Open3 class: capture*, pipline*, popen*.

The docs are quite clear, so I won't repeat them here.

Craftonix - AA
  • 410
  • 5
  • 13