2

I'm looking for something equivalent of the backticks operator (``) with the capability to display output during shell command execution.

I saw a solution in another post: (Running a command from Ruby displaying and capturing the output)

output = []
IO.popen("ruby -e '3.times{|i| p i; sleep 1}'").each do |line|
  p line.chomp
  output << line.chomp
end
p output

This solution doesn't fit my needs since $? remains nil after the shell command execution. The solution I'm looking for should also set $? (returning the value of $?.exitstatus in another way is also sufficient)

Thanks!

Community
  • 1
  • 1
miluz
  • 1,353
  • 3
  • 14
  • 22

2 Answers2

4

First, I'd recommend using one of the methods in Open3.

I use capture3 for one of my systems where we need to grab the output of STDOUT and STDERR of a lot of command-line applications.

If you need a piped sub-process, try popen3 or one of the other "pipeline" commands.

Here's some code to illustrate how to use popen2, which ignores the STDERR channel. If you want to track that also use popen3:

require 'open3'

output = []

exit_status = Open3.popen2(ENV, "ruby -e '3.times{|i| p i; sleep 1}'") { |stdin, stdout, thr|
  stdin.close
  stdout.each_line do |o|
    o.chomp!
    output << o
    puts %Q(Read from pipe: "#{ o }")
  end
  thr.value
}

puts "Output array: #{ output.join(', ') }"
puts "Exit status: #{ exit_status }"

Running that outputs:

Read from pipe: "0"
Read from pipe: "1"
Read from pipe: "2"
Output array: 0, 1, 2
Exit status: pid 43413 exit 0

The example code shows one way to do it.

It's not necessary to use each_line, but that demonstrates how you can read line-by-line until the sub-process closes its STDOUT.

capture3 doesn't accept a block; It waits until the child has closed its output and exits, then it returns the content, which is great when you want a blocking process. popen2 and popen3 have blocking and non-blocking versions, but I show only the non-blocking version here to demonstrate how to read and output the content as it comes in from the sub-process.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
3

Try following:

output = []
IO.popen("ruby -e '3.times{|i| p i; sleep 1 }'") do |f|
  f.each do |line|
    p line.chomp
    output << line.chomp
  end
end
p $?

prints

"0"
"1"
"2"
#<Process::Status: pid 2501 exit 0>

Using open3

require 'open3'

output = []
Open3.popen2("ruby -e '3.times{|i| p i; sleep 1}'") do |stdin,stdout,wait_thr|
  stdout.each do |line|
    p line.chomp
    output << line.chomp
  end
  p wait_thr.value
end
falsetru
  • 357,413
  • 63
  • 732
  • 636