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 M. 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 H. 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

On Nov 28, 2006, at 2350 , Paul L. wrote:

win32, unix, cygwin…
and vice versa.
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.


Eric H. - [email protected] - http://blog.segment7.net

I LIT YOUR GEM ON FIRE!

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

On Wed, 29 Nov 2006, Devin M. wrote:

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.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

 #
 # 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

On Wed, 29 Nov 2006, Devin M. wrote:

puts “Hey guess what: #{inp.read}”
END

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

[email protected] wrote:

On Wed, 29 Nov 2006, Devin M. wrote:

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.)