I'm a process-spawning idiot

I'm looking for a method that, like Kernel#` and IO::popen, allows me to call out to the system and grab its stdout. However, I'd like it to accept multiple args (like Kernel#system), so I don't have to worry about filenames with spaces and backslashes and the like. Alternatively, I'll take a lib that escapes strings for Kernel#`, provided it works on win32, unix, cygwin...

I'm sure I'm just missing the obvious. Help?

Thanks,
Devin

Devin Mullins wrote:

I'm looking for a method that, like Kernel#` and IO::popen, allows me to
call out to the system and grab its stdout. However, I'd like it to
accept multiple args (like Kernel#system), so I don't have to worry
about filenames with spaces and backslashes and the like. Alternatively,
I'll take a lib that escapes strings for Kernel#`, provided it works on
win32, unix, cygwin...

I'm sure I'm just missing the obvious. Help?

You want to what? You want to execute an arbitrary system command and
collect the command's output?

data = `command arg1 arg2 arg3`

Obviously if the provided command line works in a shell, it will work here,
and vice versa.

···

--
Paul Lutus
http://www.arachnoid.com

system 'command', 'arg with spaces' will execute correctly (ARGV[0] will be 'arg with spaces')

`command arg with spaces` requires manual intervention (ARGV[0] will be 'arg').

That said, try playing with shell.rb. I don't really know what it does, though.

···

On Nov 28, 2006, at 2350 , Paul Lutus wrote:

Devin Mullins wrote:

I'm looking for a method that, like Kernel#` and IO::popen, allows me to
call out to the system and grab its stdout. However, I'd like it to
accept multiple args (like Kernel#system), so I don't have to worry
about filenames with spaces and backslashes and the like. Alternatively,
I'll take a lib that escapes strings for Kernel#`, provided it works on
win32, unix, cygwin...

I'm sure I'm just missing the obvious. Help?

You want to what? You want to execute an arbitrary system command and
collect the command's output?

data = `command arg1 arg2 arg3`

Obviously if the provided command line works in a shell, it will work here,
and vice versa.

--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net

I LIT YOUR GEM ON FIRE!

Eric Hodel wrote:

That said, try playing with shell.rb. I don't really know what it does, though.

Thanks!

This Ruby program:
   STDOUT.reopen open('log.txt','w')
   exec 'echo', 'Hi!'
   __END__
outputs "Hi!" to log.txt.

shell.rb (or, more specifically, shell/process-controller.rb), uses this combined with fork (since exec replaces the process) to do its dirty work.

Downloading win32-process now... :smiley:

Devin

For reference's sake, I don't need fork. The same trick works with Kernel#system. Some ugly code:
   inp, out = IO.pipe
   # start Thread.critical
   old_stdout = STDOUT.dup
   STDOUT.reopen out
   system 'echo', 'matz is nice!'
   STDOUT.reopen old_stdout
   # end Thread.critical
   out.close
   puts "Hey guess what: #{inp.read}"
__END__

Out of curiosity, is this one of those places where I'd _have_ to use Thread.critical (assuming I don't want other threads putsing to my pipe)?

Devin

Devin Mullins wrote:

···

This Ruby program:
  STDOUT.reopen open('log.txt','w')
  exec 'echo', 'Hi!'
  __END__
outputs "Hi!" to log.txt.

shell.rb (or, more specifically, shell/process-controller.rb), uses this combined with fork (since exec replaces the process) to do its dirty work.

Downloading win32-process now... :smiley:

systemu.rb allows capture of stdout, stderr, and exit_status and specification
of stdin in an cross platform way.

NAME

   systemu.rb

SYNOPSIS

   univeral capture of stdout and stderr and handling of child process pid for windows, *nix, etc.

URIS

   http://rubyforge.org/projects/codeforpeople/
   http://codeforpeople.com/lib/ruby/

INSTALL

   gem install systemu

SAMPLES

   <========< samples/a.rb >========>

   ~ > cat samples/a.rb

···

On Wed, 29 Nov 2006, Devin Mullins wrote:

Eric Hodel wrote:

That said, try playing with shell.rb. I don't really know what it does, though.

Thanks!

This Ruby program:
STDOUT.reopen open('log.txt','w')
exec 'echo', 'Hi!'
__END__
outputs "Hi!" to log.txt.

shell.rb (or, more specifically, shell/process-controller.rb), uses this combined with fork (since exec replaces the process) to do its dirty work.

Downloading win32-process now... :smiley:

Devin

     #
     # systemu can be used on any platform to return status, stdout, and stderr of
     # any command. unlike other methods like open3/popen4 there is zero danger of
     # full pipes or threading issues hanging your process or subprocess.
     #
       require 'systemu'

       date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " )

       status, stdout, stderr = systemu date
       p [ status, stdout, stderr ]

   ~ > ruby samples/a.rb

     [#<Process::Status: pid=9960,exited(0)>, "Fri Nov 03 17:22:23 MST 2006\n", "Fri Nov 03 17:22:23 MST 2006\n"]

   <========< samples/b.rb >========>

   ~ > cat samples/b.rb

     #
     # quite a few keys can be passed to the command to alter it's behaviour. if
     # either stdout or stderr is supplied those objects should respond_to? '<<'
     # and only status will be returned
     #
       require 'systemu'

       date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " )

       stdout, stderr = '', ''
       status = systemu date, 'stdout' => stdout, 'stderr' => stderr
       p [ status, stdout, stderr ]

   ~ > ruby samples/b.rb

     [#<Process::Status: pid=9965,exited(0)>, "Fri Nov 03 17:22:23 MST 2006\n", "Fri Nov 03 17:22:23 MST 2006\n"]

   <========< samples/c.rb >========>

   ~ > cat samples/c.rb

     #
     # of course stdin can be supplied too. synonyms for 'stdin' include '0' and
     # 0. the other stdio streams have similar shortcuts
     #
       require 'systemu'

       cat = %q( ruby -e" ARGF.each{|line| puts line} " )

       status = systemu cat, 0=>'the stdin for cat', 1=>stdout=''
       puts stdout

   ~ > ruby samples/c.rb

     the stdin for cat

   <========< samples/d.rb >========>

   ~ > cat samples/d.rb

     #
     # the cwd can be supplied
     #
       require 'systemu'
       require 'tmpdir'

       pwd = %q( ruby -e" STDERR.puts Dir.pwd " )

       status = systemu pwd, 2=>(stderr=''), :cwd=>Dir.tmpdir
       puts stderr

   ~ > ruby samples/d.rb

     /tmp

   <========< samples/e.rb >========>

   ~ > cat samples/e.rb

     #
     # any environment vars specified are merged into the child's environment
     #
       require 'systemu'

       env = %q( ruby -r yaml -e" puts ENV[ 'answer' ] " )

       status = systemu env, 1=>stdout='', 'env'=>{ 'answer' => 0b101010 }
       puts stdout

   ~ > ruby samples/e.rb

     42

   <========< samples/f.rb >========>

   ~ > cat samples/f.rb

     #
     # if a block is specified then it is passed the child pid and run in a
     # background thread. note that this thread will __not__ be blocked during the
     # execution of the command so it may do useful work such as killing the child
     # if execution time passes a certain threshold
     #
       require 'systemu'

       looper = %q( ruby -e" loop{ STDERR.puts Time.now.to_i; sleep 1 } " )

       status, stdout, stderr =
         systemu looper do |cid|
           sleep 3
           Process.kill 9, cid
         end

       p [ status, stdout, stderr ]

   ~ > ruby samples/f.rb

     [#<Process::Status: pid=9985,signaled(SIGKILL=9)>, "", "1162599744\n1162599745\n1162599746\n1162599747\n"]

-a
--
if you want others to be happy, practice compassion.
if you want to be happy, practice compassion. -- the dalai lama

this is an insanely bad idea: a cross platform way to hang you system. try
this:

     harp:~ > cat a.rb
     big = 42.chr * 4242
     inp, out = IO.pipe
     old_stdout = STDOUT.dup
     STDOUT.reopen out
     system 'echo', big
     STDOUT.reopen old_stdout

     harp:~ > ruby a.rb
     # hung process

you can't keep writing to a pipe __unless__ someone is guarunteed to be reading
from the other end!

make it easy on yourself:

     harp:~ > cat a.rb
     require 'rubygems'
     require 'systemu' # gem install systemu

     systemu ['echo', 'matz is nice!'], :stdout=>STDOUT

     systemu ['echo', 'matz is nice!'], :stdout=>stdout=''
     p stdout

     systemu 'cat', :stdin=>['line1', 'line2'], :stdout=>stdout
     p stdout

     harp:~ > ruby a.rb
     matz is nice!
     "matz is nice!\n"
     "matz is nice!\nline1line2"

kind regards

-a

···

On Wed, 29 Nov 2006, Devin Mullins wrote:

For reference's sake, I don't need fork. The same trick works with Kernel#system. Some ugly code:
inp, out = IO.pipe
# start Thread.critical
old_stdout = STDOUT.dup
STDOUT.reopen out
system 'echo', 'matz is nice!'
STDOUT.reopen old_stdout
# end Thread.critical
out.close
puts "Hey guess what: #{inp.read}"
__END__

--
if you want others to be happy, practice compassion.
if you want to be happy, practice compassion. -- the dalai lama

<snip>
this is an insanely bad idea: a cross platform way to hang you system.

well, then. no need to be curt about it. :stuck_out_tongue:

gem install systemu

aha! i had a stinking suspicion i should just've emailed you, and not all of ruby-talk. thanks for the info, and the cool lib, ara.

devin
(tmp_dir? Marshal.dump? Yikes. Glad I didn't have to write that crap. Quick, someone put in an RCR for Kernel#backtick(cmd, *args) so this craziness can go away.)

···

ara.t.howard@noaa.gov wrote:

On Wed, 29 Nov 2006, Devin Mullins wrote: