Forum: Ruby Intercepting STDERR

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Ec9233451f7c6ba37a83388b87a1f565?d=identicon&s=25 Phrogz (Guest)
on 2006-03-01 18:44
(Received via mailing list)
I'm writing a Ruby script to run Lua code for TextMate. Using:
output = `lua #{filename}`
works when the file prints happily, but when an error occurs (e.g. a
syntax error in the file) the error message is printed to stderr (I
think) and 'escapes' my capture.

How/can I catch output to stderr from another system command? (I want
to use the output later, I just don't want it spat out at that point.)
9358cc96c46055cd68d4a76a9aefe026?d=identicon&s=25 Daniel Harple (Guest)
on 2006-03-01 19:14
(Received via mailing list)
On Mar 1, 2006, at 6:43 PM, Phrogz wrote:

> How/can I catch output to stderr from another system command? (I want
> to use the output later, I just don't want it spat out at that point.)

You could do:

output = `command 2>&1`

However, now $stderr is mixed in with $stdout.

-- Daniel
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-03-01 19:58
(Received via mailing list)
On Thu, 2 Mar 2006, Phrogz wrote:

> I'm writing a Ruby script to run Lua code for TextMate. Using:
> output = `lua #{filename}`
> works when the file prints happily, but when an error occurs (e.g. a
> syntax error in the file) the error message is printed to stderr (I
> think) and 'escapes' my capture.
>
> How/can I catch output to stderr from another system command? (I want
> to use the output later, I just don't want it spat out at that point.)
>
>

   harp:~ > gem install session >/dev/null 2>&1 && echo 'success'
   success


   harp:~ > cat a.rb
   require 'rubygems'
   require 'session'
   sh = Session::new
   command = %Q( ruby -e" STDERR.puts :stderr; STDOUT.puts :stdout  " )
   stdout, stderr = sh.execute command

   require 'yaml'
   y "stdout" => stdout
   y "stderr" => stderr
   y "sh.status" => sh.status


   harp:~ > ruby a.rb
   ---
   stdout: |
     stdout

   ---
   stderr: |
     stderr

   ---
   sh.status: 0


-a
Ec9233451f7c6ba37a83388b87a1f565?d=identicon&s=25 Phrogz (Guest)
on 2006-03-01 19:58
(Received via mailing list)
That'll do for now, thanks! :)
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-03-01 20:20
(Received via mailing list)
On Mar 1, 2006, at 1:58 PM, Phrogz wrote:

> That'll do for now, thanks! :)
>
>

You may want to check out IO.popen and IO.popen3 (or is it popen2? I
can never remembr the numbers)
Ec9233451f7c6ba37a83388b87a1f565?d=identicon&s=25 Phrogz (Guest)
on 2006-03-02 05:58
(Received via mailing list)
That's awesome, ara. Unfortunately, I need to write something that will
work on random MacOS machines witj a default Ruby install. Can I poke
about in the innards of session and steal ideas/code?
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-03-02 06:21
(Received via mailing list)
On Thu, 2 Mar 2006, Phrogz wrote:

> That's awesome, ara. Unfortunately, I need to write something that will
> work on random MacOS machines witj a default Ruby install. Can I poke
> about in the innards of session and steal ideas/code?

absolutely.  basically what you're after is open3 - it's in the stdlib
and
will probably suffice.  session is overkill if you're not running
multiple
commands per session (no pun intended) anyhow.

anyhow, here's the essence:

     harp:~ > cat a.rb
     def spawn command, opts = {}
       require 'open3'
       stdin = opts.values_at(:stdin, 'stdin', 0).compact.first
       stdout = opts.values_at(:stdout, 'stdout', 1).compact.first
       stderr = opts.values_at(:stderr, 'stderr', 2).compact.first

       Open3::popen3(command) do |i,o,e|
         i << stdin if stdin
         i.close # important!
         o.each{|line| stdout << line} if stdout
         e.each{|line| stderr << wrine} if stderr
       end

       $?.exitstatus
     end

     stdout, stderr = '', ''
     exitstatus = spawn 'cat', 0=>42, 1=>stdout, 2=>stderr

     require 'yaml'
     y 'exitstatus' => exitstatus,
       'stdout'     => stdout,
       'stderr'     => stderr


     harp:~ > ruby a.rb
     ---
     stdout: "42"
     stderr: ""
     exitstatus: 0


regards.

-a
Ec9233451f7c6ba37a83388b87a1f565?d=identicon&s=25 Phrogz (Guest)
on 2006-03-04 09:14
(Received via mailing list)
Thanks so much, Ara. That's almost perfect, except that the exitstatus
seems to be eaten. No matter what happens, I get a clean 0 exitstatus.
For example:

require 'open3'

`lua xxx`
#=> lua: cannot open xxx: No such file or directory

p $?.exitstatus
#=> 1

output, errors = '', ''
p Open3::popen3( "lua xxx" ){ |i,o,e|
	i.close
	o.each { |line| output << line }
	e.each { |line| errors << line }
	$?.exitstatus
}
#=> 0


I can mostly infer the failure based on whether or not messages came to
stderr, but it'd be nice to know for sure.
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-03-04 17:02
(Received via mailing list)
On Sat, 4 Mar 2006, Phrogz wrote:

> #=> 1
>
> I can mostly infer the failure based on whether or not messages came to
> stderr, but it'd be nice to know for sure.
>
>

right you are.  if you check out ruby-1.8.4/lib/open3.rb you'll see :

...
     pid = fork{
       # child
       fork{
 	# grandchild
         ...
         ...
         ...
       }
       exit!(0)        # check this out
     }
...

so you'll never see an error.  my open 4 package fixes this - i actually
just
made a release a few weeks ago to support throwing and exception when
the exec
fails.  here's an example (it's on rubyforge):

     harp:~ > cat a.rb
     IO::popen("gem install open4 -r -y"){|pipe| pipe.read} and puts
"installed open4..."
     require "rubygems"
     require "open4"

     def spawn cmd, opts = {}
       getopt = lambda{|o,*d| opts[o] || opts[o.to_s] ||
opts[o.to_s.intern] || d.shift}
       stdin = getopt["stdin"] || getopt[0, ""]
       stdout = getopt["stdout"] || getopt[1, ""]
       stderr = getopt["stderr"] || getopt[2, ""]

       Open4::popen4(cmd) do |cid,i,o,e|
         stdin.each{|line| i << line}
         i.close
         o.each{|line| stdout << line }
         e.each{|line| stderr << line }
       end

       [$?.exitstatus, stdout, stderr]
     end


     bt =
       lambda{|e| STDERR.puts %Q[#{ e.message } (#{ e.class })\n#{
e.backtrace.join "\n" }\n]}


     argvs =
       ["fubar"],
       ["ruby does-not-exist"],
       ["ruby", {"stdin" => "p 42"}]


     argvs.each do |argv|
       cmd, opts = argv
       puts
       puts "--- spawn( #{ cmd.inspect }, #{ opts.inspect } ) ---"
       p( spawn(*argv) ) rescue bt[$!]
       puts
       puts
     end



     harp:~ > ruby a.rb
     installed open4...

     --- spawn( "fubar", nil ) ---
     No such file or directory - fubar (Errno::ENOENT)
     /home/ahoward//lib/ruby/site_ruby/1.8/open4.rb:75:in `exec'
     /home/ahoward//lib/ruby/site_ruby/1.8/open4.rb:75:in `popen4'
     /home/ahoward//lib/ruby/site_ruby/1.8/open4.rb:57:in `popen4'
     a.rb:11:in `spawn'
     a.rb:36
     a.rb:32



     --- spawn( "ruby does-not-exist", nil ) ---
     [1, "", "ruby: No such file or directory -- does-not-exist
(LoadError)\n"]



     --- spawn( "ruby", {"stdin"=>"p 42"} ) ---
     [0, "42\n", ""]


hth.

i'd sure like to get this in the stdlib...

-a
This topic is locked and can not be replied to.