Incerteza
Incerteza

Reputation: 34884

Run a process, capture its output and exit code

I know how to run an external process as there are many ways to do so. But how do I capture its output and exit code to the variables? These, the most popular ways of running a process, don't work as I want:

a = `ls -l` # "a" captures only output
b = system "ls -l " # "b" captures only exit code

Upvotes: 2

Views: 733

Answers (5)

7stud
7stud

Reputation: 48599

I want exception as an instance of class Exception to be able to re-raise it, not just a string.

You can do this:

#my_prog.rb

puts 'hello'
10/0

...

require 'open3'

begin
  Open3.popen3("ruby my_prog.rb") do |stdin, stdout, stderr, wait_thr|
    puts stdout.read
    status_obj = wait_thr.value  #Process::Status object returned.
    puts status_obj.exitstatus

    error = stderr.read
    md = error.match(/\w+ Error/xm)

    if md
      e = Exception.const_get(md[0]).new if md 
      p e.class.ancestors
      raise e
    end

  end
rescue ZeroDivisionError
  puts 'Hey, someone divided by zero!'
end

--output:--
hello
1
[ZeroDivisionError, StandardError, Exception, Object, Kernel, BasicObject]
Hey, someone divided by zero!

Unfortunately, there are several exceptions whose names do not end in 'Error'--but you can modify the code to search for those specifically in the error message.

Upvotes: 0

Arup Rakshit
Arup Rakshit

Reputation: 118271

Here is a way to get this out.

#!/usr/bin/env ruby

require 'open3'

Open3.popen3("ls -l") do |stdin, stdout, stderr, wait_thr|
  puts stdout.read
  puts wait_thr.value.exitstatus 
end

# >> total 52
# >> -rw-r--r-- 1 arup users    0 Aug  5 09:32 a.rb
# >> drwxr-xr-x 2 arup users 4096 Jul 20 20:37 FSS
# >> drwxr-xr-x 2 arup users 4096 Jul 20 20:37 fss_dir
# >> -rw-r--r-- 1 arup users   42 Jul 19 01:36 out.txt
# .....
#...
# >> 0

Doco is very clear of ::popen3. stdout gives IO object. So, we need to use IO#read method. And wait_thr.value gives us Process::Status. Now once we have that object, we can use #exitstatus method for the same.

Upvotes: -1

konsolebox
konsolebox

Reputation: 75488

Most appropriate is to read $?.exitstatus:

a = `ls -l`        # gets output
b = $?.exitstatus  # gets exit code

Test:

`true`
# => ""
$?.exitstatus
# => 0
`false`
# => ""
$?.exitstatus
# => 1
$?.class
# => Process::Status

See Process::Status for more ways to handle exit status.

Upvotes: 5

7stud
7stud

Reputation: 48599

#my_prog.rb
puts 'hello'
exit 10

...

require 'open3'

Open3.popen2("ruby my_prog.rb") do |stdin, stdout, wait_thr|
  puts stdout.read
  status_obj = wait_thr.value # Process::Status object returned.
  puts status_obj.exitstatus
end


--output:--
hello
10

Upvotes: 0

briantist
briantist

Reputation: 47792

I would have a look at the popen4 gem, as it will allow you to get stout/stderr and the exit status.

Also see this page describing 6 Ways to Run Shell Commands in Ruby which also details popen4 usage.

status = Open4::popen4("false") do |pid, stdin, stdout, stderr|
    puts "stdout: #{stdout}" 
end
puts status

Upvotes: 1

Related Questions