I'm a process-spawning idiot.

D

Devin Mullins

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
 
E

Eric Hodel

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.

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

Devin Mullins

Eric said:
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... :D

Devin
 
D

Devin Mullins

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
 
A

ara.t.howard

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

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
 
A

ara.t.howard

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__

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
 
D

Devin Mullins

On Wed, 29 Nov 2006, Devin Mullins wrote:
<snip>
this is an insanely bad idea: a cross platform way to hang you system.
well, then. no need to be curt about it. :p
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.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,077
Latest member
SangMoor21

Latest Threads

Top