Getting stdout and stderr for system calls on windows

OK, I'm stuck with a pretty problem:
I need to drive a build process using make (dmake) from my scripts and I want to capture stdout and stderr as well as the return value from dmake.
Normally I would do it using Daniel Berger's win32-popen3, but I have a Windows Installer Ruby with VC++6.0 which nastily gives me a segfault (properly documented in the README, but still a segfault :slight_smile: ).
Recompiling Ruby is a no-no and changing VC compiler also a no-no.

Now, does anyone have a quick solution for me?
Is there anyway to call say:

system 'echo blabla'

and to redirect stdout and stderr used by system? (I guess not).
Any help is appreciated.
I am aware of

IO.popen("dmake target 2>&1") {|f|
     output = f.read
     exitcode = Process.waitpid2(f.pid)[1]
   }
and is currently my best choice, although I would like to split stderr and stdout.

Cheers,
V.-

···

--
http://www.braveworld.net/riva

____________________________________________________________________
http://www.freemail.gr - äùñåÜí õðçñåóßá çëåêôñïíéêïý ôá÷õäñïìåßïõ.
http://www.freemail.gr - free email service for the Greek-speaking.

i was working on this at one point:

   harp:~ > cat a.rb
   class Redirector
     require "tempfile"
     attr_accessor "ruby"
     def initialize
       @ruby = "ruby"
       @script = tempfile
       @script.write <<-ruby
         stdout, stderr = ARGV.shift, ARGV.shift
         File::unlink out rescue nil
         File::unlink err rescue nil
         STDOUT.reopen(open(stdout,"w"))
         STDERR.reopen(open(stderr,"w"))
         system(ARGV.join(' '))
       ruby
       @script.close
     end
     def run command, redirects = {}
       stdout = redirects.values_at("stdout", :stdout, "o", :o, 1).compact.first
       tout = nil
       unless stdout
         tout = tempfile
         stdout = tout.path
       end
       stderr = redirects.values_at("stderr", :stderr, "e", :e, 2).compact.first
       terr = nil
       unless stderr
         terr = tempfile
         stderr = terr.path
       end
       system "#{ @ruby } #{ @script.path } #{ stdout } #{ stderr } #{ command }"
       ret = IO::read(stdout), IO::read(stderr), $?.exitstatus
       tout.close! if tout
       terr.close! if terr
       ret
     end
     def tempfile
       Tempfile::new(Process::pid.to_s << rand.to_s)
     end
   end

   redirector = Redirector::new

   stdout, stderr, exitstatus = redirector.run "echo 42"
   p [stdout, stderr, exitstatus]

   redirector.run "echo 42", 1 => "out", 2 => "err"
   p [IO::read("out"), IO::read("err")]

   harp:~ > ruby a.rb
   ["42\n", "", 0]
   ["42\n", ""]

regards.

-a

···

On Thu, 24 Nov 2005, Damphyr wrote:

OK, I'm stuck with a pretty problem:
I need to drive a build process using make (dmake) from my scripts and I
want to capture stdout and stderr as well as the return value from dmake.
Normally I would do it using Daniel Berger's win32-popen3, but I have a
Windows Installer Ruby with VC++6.0 which nastily gives me a segfault
(properly documented in the README, but still a segfault :slight_smile: ). Recompiling
Ruby is a no-no and changing VC compiler also a no-no.

Now, does anyone have a quick solution for me? Is there anyway to call say:

system 'echo blabla'

and to redirect stdout and stderr used by system? (I guess not).
Any help is appreciated.
I am aware of

IO.popen("dmake target 2>&1") {|f|
   output = f.read
   exitcode = Process.waitpid2(f.pid)[1]
}
and is currently my best choice, although I would like to split stderr and stdout.

Cheers,
V.-

--

ara [dot] t [dot] howard [at] noaa [dot] gov
all happiness comes from the desire for others to be happy. all misery
comes from the desire for oneself to be happy.
-- bodhicaryavatara

===============================================================================

I have the same problem, and i'm likely going to end up using this
syntax inside a system call:

(((dmake target | tee stdout.txt) 3>&1 1>&2 2>&3 | tee stderr.txt) 3>&1
1>&2 2>&3) 2>&1 | tee output.txt

where output.txt will contain stderr + stdout
stdout.txt will contain stdout
stderr.txt will contain stderr

There is a description of how this works here:

Scroll down to "Capturing stderr with tee Swapping stderr and stdout"
Ugly as hell but it does the trick... hopefully someone knows a cleaner
way to do this within ruby?

···

--

ara.t.howard wrote:

On Thu, 24 Nov 2005, Damphyr wrote:

system 'echo blabla'
stdout.

Cheers,
V.-

i was working on this at one point:

   harp:~ > cat a.rb
   class Redirector
     require "tempfile"
     attr_accessor "ruby"
     def initialize
       @ruby = "ruby"
       @script = tempfile
       @script.write <<-ruby
         stdout, stderr = ARGV.shift, ARGV.shift
         File::unlink out rescue nil
         File::unlink err rescue nil
         STDOUT.reopen(open(stdout,"w"))
         STDERR.reopen(open(stderr,"w"))
         system(ARGV.join(' '))
       ruby
       @script.close
     end
     def run command, redirects = {}
       stdout = redirects.values_at("stdout", :stdout, "o", :o,
1).compact.first
       tout = nil
       unless stdout
         tout = tempfile
         stdout = tout.path
       end
       stderr = redirects.values_at("stderr", :stderr, "e", :e,
2).compact.first
       terr = nil
       unless stderr
         terr = tempfile
         stderr = terr.path
       end
       system "#{ @ruby } #{ @script.path } #{ stdout } #{ stderr } #{
command }"
       ret = IO::read(stdout), IO::read(stderr), $?.exitstatus
       tout.close! if tout
       terr.close! if terr
       ret
     end
     def tempfile
       Tempfile::new(Process::pid.to_s << rand.to_s)
     end
   end

   redirector = Redirector::new

   stdout, stderr, exitstatus = redirector.run "echo 42"
   p [stdout, stderr, exitstatus]

   redirector.run "echo 42", 1 => "out", 2 => "err"
   p [IO::read("out"), IO::read("err")]

   harp:~ > ruby a.rb
   ["42\n", "", 0]
   ["42\n", ""]

regards.

-a

--
Posted via http://www.ruby-forum.com/\.

I ended up with the following after this discussion. It doesn't separate stderr and stdout, but for what I want it, it's more than enough. And I added crude benchmarking as well:) :
There was also an exitcode method in there (you can get the exitcde from @process).

#Executes a command on a dos shell, redirecting stderr to stdout ("2>&1")

···

#
#You can then access the output and the return value for the command.
#
#This is meant as a last-resort replacement for popen3 (because of problems with VC++6.0 and the Ruby One-Click Installer).
#
#_exec_time_ provides the Time spent running the command.
class ExecCmd
  attr_reader :output,:cmd,:exec_time
  #When a block is given, the command runs before yielding
  def initialize cmd
    @output=""
    @exec_time=0
    @cmd=cmd
    @cmd_run=cmd+" 2>&1" unless cmd=~/2>&1/
    if block_given?
      run
      yield self
    end
  end
  
  #Runs the command
  def run
    t1=Time.now
    IO.popen(@cmd_run){|f|
      @output=f.read
      @process=Process.waitpid2(f.pid)[1]
    }
    @exec_time=Time.now-t1
  end
  #Returns false if the command hasn't been executed yet
  def run?
    return false unless @process
    return true
  end
  #Returns true if the command was succesfull.
  #
  #Will return false if the command hasn't been executed
  def success?
    return @process.success? if @process
    return false
  end
end

www-data wrote:

I have the same problem, and i'm likely going to end up using this syntax inside a system call:

(((dmake target | tee stdout.txt) 3>&1 1>&2 2>&3 | tee stderr.txt) 3>&1 1>&2 2>&3) 2>&1 | tee output.txt

where output.txt will contain stderr + stdout
stdout.txt will contain stdout
stderr.txt will contain stderr

There is a description of how this works here:
http://www.cpqlinux.com/redirect.html
Scroll down to "Capturing stderr with tee Swapping stderr and stdout"
Ugly as hell but it does the trick... hopefully someone knows a cleaner way to do this within ruby?

--

ara.t.howard wrote:

On Thu, 24 Nov 2005, Damphyr wrote:

system 'echo blabla'
stdout.

Cheers,
V.-

i was working on this at one point:

  harp:~ > cat a.rb
  class Redirector
    require "tempfile"
    attr_accessor "ruby"
    def initialize
      @ruby = "ruby"
      @script = tempfile
      @script.write <<-ruby
        stdout, stderr = ARGV.shift, ARGV.shift
        File::unlink out rescue nil
        File::unlink err rescue nil
        STDOUT.reopen(open(stdout,"w"))
        STDERR.reopen(open(stderr,"w"))
        system(ARGV.join(' '))
      ruby
      @script.close
    end
    def run command, redirects = {}
      stdout = redirects.values_at("stdout", :stdout, "o", :o, 1).compact.first
      tout = nil
      unless stdout
        tout = tempfile
        stdout = tout.path
      end
      stderr = redirects.values_at("stderr", :stderr, "e", :e, 2).compact.first
      terr = nil
      unless stderr
        terr = tempfile
        stderr = terr.path
      end
      system "#{ @ruby } #{ @script.path } #{ stdout } #{ stderr } #{ command }"
      ret = IO::read(stdout), IO::read(stderr), $?.exitstatus
      tout.close! if tout
      terr.close! if terr
      ret
    end
    def tempfile
      Tempfile::new(Process::pid.to_s << rand.to_s)
    end
  end

  redirector = Redirector::new

  stdout, stderr, exitstatus = redirector.run "echo 42"
  p [stdout, stderr, exitstatus]

  redirector.run "echo 42", 1 => "out", 2 => "err"
  p [IO::read("out"), IO::read("err")]

  harp:~ > ruby a.rb
  ["42\n", "", 0]
  ["42\n", ""]

regards.

-a

--
http://www.braveworld.net/riva

____________________________________________________________________
http://www.freemail.gr - äùñåÜí õðçñåóßá çëåêôñïíéêïý ôá÷õäñïìåßïõ.
http://www.freemail.gr - free email service for the Greek-speaking.