systemu 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,149 @@
1
+ NAME
2
+
3
+ systemu.rb
4
+
5
+ SYNOPSIS
6
+
7
+ univeral capture of stdout and stderr and handling of child process pid for windows, *nix, etc.
8
+
9
+ URIS
10
+
11
+ http://rubyforge.org/projects/codeforpeople/
12
+ http://codeforpeople.com/lib/ruby/
13
+
14
+ INSTALL
15
+
16
+ gem install systemu
17
+
18
+ SAMPLES
19
+
20
+ <========< samples/a.rb >========>
21
+
22
+ ~ > cat samples/a.rb
23
+
24
+ #
25
+ # systemu can be used on any platform to return status, stdout, and stderr of
26
+ # any command. unlike other methods like open3/popen4 there is zero danger of
27
+ # full pipes or threading issues hanging your process or subprocess.
28
+ #
29
+ require 'systemu'
30
+
31
+ date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " )
32
+
33
+ status, stdout, stderr = systemu date
34
+ p [ status, stdout, stderr ]
35
+
36
+ ~ > ruby samples/a.rb
37
+
38
+ [#<Process::Status: pid=9960,exited(0)>, "Fri Nov 03 17:22:23 MST 2006\n", "Fri Nov 03 17:22:23 MST 2006\n"]
39
+
40
+
41
+ <========< samples/b.rb >========>
42
+
43
+ ~ > cat samples/b.rb
44
+
45
+ #
46
+ # quite a few keys can be passed to the command to alter it's behaviour. if
47
+ # either stdout or stderr is supplied those objects should respond_to? '<<'
48
+ # and only status will be returned
49
+ #
50
+ require 'systemu'
51
+
52
+ date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " )
53
+
54
+ stdout, stderr = '', ''
55
+ status = systemu date, 'stdout' => stdout, 'stderr' => stderr
56
+ p [ status, stdout, stderr ]
57
+
58
+ ~ > ruby samples/b.rb
59
+
60
+ [#<Process::Status: pid=9965,exited(0)>, "Fri Nov 03 17:22:23 MST 2006\n", "Fri Nov 03 17:22:23 MST 2006\n"]
61
+
62
+
63
+ <========< samples/c.rb >========>
64
+
65
+ ~ > cat samples/c.rb
66
+
67
+ #
68
+ # of course stdin can be supplied too. synonyms for 'stdin' include '0' and
69
+ # 0. the other stdio streams have similar shortcuts
70
+ #
71
+ require 'systemu'
72
+
73
+ cat = %q( ruby -e" ARGF.each{|line| puts line} " )
74
+
75
+ status = systemu cat, 0=>'the stdin for cat', 1=>stdout=''
76
+ puts stdout
77
+
78
+ ~ > ruby samples/c.rb
79
+
80
+ the stdin for cat
81
+
82
+
83
+ <========< samples/d.rb >========>
84
+
85
+ ~ > cat samples/d.rb
86
+
87
+ #
88
+ # the cwd can be supplied
89
+ #
90
+ require 'systemu'
91
+ require 'tmpdir'
92
+
93
+ pwd = %q( ruby -e" STDERR.puts Dir.pwd " )
94
+
95
+ status = systemu pwd, 2=>(stderr=''), :cwd=>Dir.tmpdir
96
+ puts stderr
97
+
98
+
99
+ ~ > ruby samples/d.rb
100
+
101
+ /tmp
102
+
103
+
104
+ <========< samples/e.rb >========>
105
+
106
+ ~ > cat samples/e.rb
107
+
108
+ #
109
+ # any environment vars specified are merged into the child's environment
110
+ #
111
+ require 'systemu'
112
+
113
+ env = %q( ruby -r yaml -e" puts ENV[ 'answer' ] " )
114
+
115
+ status = systemu env, 1=>stdout='', 'env'=>{ 'answer' => 0b101010 }
116
+ puts stdout
117
+
118
+ ~ > ruby samples/e.rb
119
+
120
+ 42
121
+
122
+
123
+ <========< samples/f.rb >========>
124
+
125
+ ~ > cat samples/f.rb
126
+
127
+ #
128
+ # if a block is specified then it is passed the child pid and run in a
129
+ # background thread. note that this thread will __not__ be blocked during the
130
+ # execution of the command so it may do useful work such as killing the child
131
+ # if execution time passes a certain threshold
132
+ #
133
+ require 'systemu'
134
+
135
+ looper = %q( ruby -e" loop{ STDERR.puts Time.now.to_i; sleep 1 } " )
136
+
137
+ status, stdout, stderr =
138
+ systemu looper do |cid|
139
+ sleep 3
140
+ Process.kill 9, cid
141
+ end
142
+
143
+ p [ status, stdout, stderr ]
144
+
145
+
146
+ ~ > ruby samples/f.rb
147
+
148
+ [#<Process::Status: pid=9985,signaled(SIGKILL=9)>, "", "1162599744\n1162599745\n1162599746\n1162599747\n"]
149
+
@@ -0,0 +1,20 @@
1
+ NAME
2
+
3
+ systemu.rb
4
+
5
+ SYNOPSIS
6
+
7
+ univeral capture of stdout and stderr and handling of child process pid for windows, *nix, etc.
8
+
9
+ URIS
10
+
11
+ http://rubyforge.org/projects/codeforpeople/
12
+ http://codeforpeople.com/lib/ruby/
13
+
14
+ INSTALL
15
+
16
+ gem install systemu
17
+
18
+ SAMPLES
19
+
20
+ @samples
@@ -0,0 +1,23 @@
1
+ lib, version = File::basename(File::dirname(File::expand_path(__FILE__))).split %r/-/, 2
2
+
3
+ require 'rubygems'
4
+
5
+ Gem::Specification::new do |spec|
6
+ spec.name = lib
7
+ spec.version = version
8
+ spec.platform = Gem::Platform::RUBY
9
+ spec.summary = lib
10
+
11
+ spec.files = Dir::glob "**/**"
12
+ spec.executables = Dir::glob("bin/*").map{|exe| File::basename exe}
13
+
14
+ spec.require_path = "lib"
15
+ spec.autorequire = lib
16
+
17
+ spec.has_rdoc = File::exist? "doc"
18
+ spec.test_suite_file = "test/#{ lib }.rb" if File::directory? "test"
19
+
20
+ spec.author = "Ara T. Howard"
21
+ spec.email = "ara.t.howard@noaa.gov"
22
+ spec.homepage = "http://codeforpeople.com/lib/ruby/#{ lib }/"
23
+ end
@@ -0,0 +1,32 @@
1
+ require 'pathname'
2
+
3
+ $VERBOSE=nil
4
+
5
+ def indent s, n = 2
6
+ ws = ' ' * n
7
+ s.gsub %r/^/, ws
8
+ end
9
+
10
+ template = IO::read 'README.tmpl'
11
+
12
+ samples = ''
13
+ prompt = '~ > '
14
+
15
+ Dir['sample*/*'].sort.each do |sample|
16
+ samples << "\n" << " <========< #{ sample } >========>" << "\n\n"
17
+
18
+ cmd = "cat #{ sample }"
19
+ samples << indent(prompt + cmd, 2) << "\n\n"
20
+ samples << indent(`#{ cmd }`, 4) << "\n"
21
+
22
+ cmd = "ruby #{ sample }"
23
+ samples << indent(prompt + cmd, 2) << "\n\n"
24
+
25
+ cmd = "ruby -W0 -Ilib #{ sample }"
26
+ samples << indent(`#{ cmd } 2>&1`, 4) << "\n"
27
+ end
28
+
29
+ #samples.gsub! %r/^/, ' '
30
+
31
+ readme = template.gsub %r/^\s*@samples\s*$/, samples
32
+ print readme
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rbconfig'
3
+ require 'find'
4
+ require 'ftools'
5
+ require 'tempfile'
6
+ include Config
7
+
8
+ LIBDIR = "lib"
9
+ LIBDIR_MODE = 0644
10
+
11
+ BINDIR = "bin"
12
+ BINDIR_MODE = 0755
13
+
14
+
15
+ $srcdir = CONFIG["srcdir"]
16
+ $version = CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
17
+ $libdir = File.join(CONFIG["libdir"], "ruby", $version)
18
+ $archdir = File.join($libdir, CONFIG["arch"])
19
+ $site_libdir = $:.find {|x| x =~ /site_ruby$/}
20
+ $bindir = CONFIG["bindir"] || CONFIG['BINDIR']
21
+ $ruby_install_name = CONFIG['ruby_install_name'] || CONFIG['RUBY_INSTALL_NAME'] || 'ruby'
22
+ $ruby_ext = CONFIG['EXEEXT'] || ''
23
+ $ruby = File.join($bindir, ($ruby_install_name + $ruby_ext))
24
+
25
+ if !$site_libdir
26
+ $site_libdir = File.join($libdir, "site_ruby")
27
+ elsif $site_libdir !~ %r/#{Regexp.quote($version)}/
28
+ $site_libdir = File.join($site_libdir, $version)
29
+ end
30
+
31
+ def install_rb(srcdir=nil, destdir=nil, mode=nil, bin=nil)
32
+ #{{{
33
+ path = []
34
+ dir = []
35
+ Find.find(srcdir) do |f|
36
+ next unless FileTest.file?(f)
37
+ next if (f = f[srcdir.length+1..-1]) == nil
38
+ next if (/CVS$/ =~ File.dirname(f))
39
+ next if f =~ %r/\.lnk/
40
+ path.push f
41
+ dir |= [File.dirname(f)]
42
+ end
43
+ for f in dir
44
+ next if f == "."
45
+ next if f == "CVS"
46
+ File::makedirs(File.join(destdir, f))
47
+ end
48
+ for f in path
49
+ next if (/\~$/ =~ f)
50
+ next if (/^\./ =~ File.basename(f))
51
+ unless bin
52
+ File::install(File.join(srcdir, f), File.join(destdir, f), mode, true)
53
+ else
54
+ from = File.join(srcdir, f)
55
+ to = File.join(destdir, f)
56
+ shebangify(from) do |sf|
57
+ $deferr.print from, " -> ", File::catname(from, to), "\n"
58
+ $deferr.printf "chmod %04o %s\n", mode, to
59
+ File::install(sf, to, mode, false)
60
+ end
61
+ end
62
+ end
63
+ #}}}
64
+ end
65
+ def shebangify f
66
+ #{{{
67
+ open(f) do |fd|
68
+ buf = fd.read 42
69
+ if buf =~ %r/^\s*#\s*!.*ruby/o
70
+ ftmp = Tempfile::new("#{ $$ }_#{ File::basename(f) }")
71
+ begin
72
+ fd.rewind
73
+ ftmp.puts "#!#{ $ruby }"
74
+ while((buf = fd.read(8192)))
75
+ ftmp.write buf
76
+ end
77
+ ftmp.close
78
+ yield ftmp.path
79
+ ensure
80
+ ftmp.close!
81
+ end
82
+ else
83
+ yield f
84
+ end
85
+ end
86
+ #}}}
87
+ end
88
+ def ARGV.switch
89
+ #{{{
90
+ return nil if self.empty?
91
+ arg = self.shift
92
+ return nil if arg == '--'
93
+ if arg =~ /^-(.)(.*)/
94
+ return arg if $1 == '-'
95
+ raise 'unknown switch "-"' if $2.index('-')
96
+ self.unshift "-#{$2}" if $2.size > 0
97
+ "-#{$1}"
98
+ else
99
+ self.unshift arg
100
+ nil
101
+ end
102
+ #}}}
103
+ end
104
+ def ARGV.req_arg
105
+ #{{{
106
+ self.shift || raise('missing argument')
107
+ #}}}
108
+ end
109
+ def linkify d, linked = []
110
+ #--{{{
111
+ if test ?d, d
112
+ versioned = Dir[ File::join(d, "*-[0-9].[0-9].[0-9].rb") ]
113
+ versioned.each do |v|
114
+ src, dst = v, v.gsub(%r/\-[\d\.]+\.rb$/, '.rb')
115
+ lnk = nil
116
+ begin
117
+ if test ?l, dst
118
+ lnk = "#{ dst }.lnk"
119
+ puts "#{ dst } -> #{ lnk }"
120
+ File::rename dst, lnk
121
+ end
122
+ unless test ?e, dst
123
+ puts "#{ src } -> #{ dst }"
124
+ File::copy src, dst
125
+ linked << dst
126
+ end
127
+ ensure
128
+ if lnk
129
+ at_exit do
130
+ puts "#{ lnk } -> #{ dst }"
131
+ File::rename lnk, dst
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+ linked
138
+ #--}}}
139
+ end
140
+
141
+
142
+ #
143
+ # main program
144
+ #
145
+
146
+ libdir = $site_libdir
147
+ bindir = $bindir
148
+ no_linkify = false
149
+ linked = nil
150
+ help = false
151
+
152
+ usage = <<-usage
153
+ #{ File::basename $0 }
154
+ -d, --destdir <destdir>
155
+ -l, --libdir <libdir>
156
+ -b, --bindir <bindir>
157
+ -r, --ruby <ruby>
158
+ -n, --no_linkify
159
+ -s, --sudo
160
+ -h, --help
161
+ usage
162
+
163
+ begin
164
+ while switch = ARGV.switch
165
+ case switch
166
+ when '-d', '--destdir'
167
+ libdir = ARGV.req_arg
168
+ when '-l', '--libdir'
169
+ libdir = ARGV.req_arg
170
+ when '-b', '--bindir'
171
+ bindir = ARGV.req_arg
172
+ when '-r', '--ruby'
173
+ $ruby = ARGV.req_arg
174
+ when '-n', '--no_linkify'
175
+ no_linkify = true
176
+ when '-s', '--sudo'
177
+ sudo = 'sudo'
178
+ when '-h', '--help'
179
+ help = true
180
+ else
181
+ raise "unknown switch #{switch.dump}"
182
+ end
183
+ end
184
+ rescue
185
+ STDERR.puts $!.to_s
186
+ STDERR.puts usage
187
+ exit 1
188
+ end
189
+
190
+ if help
191
+ STDOUT.puts usage
192
+ exit
193
+ end
194
+
195
+ unless no_linkify
196
+ linked = linkify('lib') + linkify('bin')
197
+ end
198
+
199
+ system "#{ $ruby } extconf.rb && make && #{ sudo } make install" if test(?s, 'extconf.rb')
200
+
201
+ install_rb(LIBDIR, libdir, LIBDIR_MODE)
202
+ install_rb(BINDIR, bindir, BINDIR_MODE, bin=true)
203
+
204
+ if linked
205
+ linked.each{|path| File::rm_f path}
206
+ end
@@ -0,0 +1,262 @@
1
+ require 'tmpdir'
2
+ require 'socket'
3
+ require 'fileutils'
4
+ require 'rbconfig'
5
+ require 'thread'
6
+
7
+ class Object
8
+ def systemu(*a, &b) SystemUniversal.new(*a, &b).systemu end
9
+ end
10
+
11
+ class SystemUniversal
12
+ #--{{{
13
+ VERSION = '1.0.0'
14
+ def version() VERSION end
15
+ #
16
+ # class methods
17
+ #
18
+
19
+ @host = Socket.gethostname
20
+ @ppid = Process.ppid
21
+ @pid = Process.pid
22
+
23
+ c = ::Config::CONFIG
24
+ ruby = File.join(c['bindir'], c['ruby_install_name']) << c['EXEEXT']
25
+ @ruby = if system('%s -e 42' % ruby)
26
+ ruby
27
+ else
28
+ system('%s -e 42' % 'ruby') ? 'ruby' : warn('no ruby in PATH/CONFIG')
29
+ end
30
+
31
+ class << self
32
+ %w( host ppid pid ruby ).each{|a| attr_accessor a}
33
+ end
34
+
35
+ #
36
+ # instance methods
37
+ #
38
+
39
+ def initialize argv, opts = {}, &block
40
+ #--{{{
41
+ getopt = getopts opts
42
+
43
+ @argv = argv
44
+ @block = block
45
+
46
+ @stdin = getopt[ ['stdin', 'in', '0', 0] ]
47
+ @stdout = getopt[ ['stdout', 'out', '1', 1] ]
48
+ @stderr = getopt[ ['stderr', 'err', '2', 2] ]
49
+ @env = getopt[ 'env' ]
50
+ @cwd = getopt[ 'cwd' ]
51
+
52
+ @host = getopt[ 'host', self.class.host ]
53
+ @ppid = getopt[ 'ppid', self.class.ppid ]
54
+ @pid = getopt[ 'pid', self.class.pid ]
55
+ @ruby = getopt[ 'ruby', self.class.ruby ]
56
+ #--}}}
57
+ end
58
+
59
+ def systemu
60
+ #--{{{
61
+ tmpdir do |tmp|
62
+ c = child_setup tmp
63
+ status = nil
64
+
65
+ if @block
66
+ q = Queue.new
67
+ t = Thread.new{ @block[ q.pop ] }
68
+ end
69
+
70
+ begin
71
+ quietly{
72
+ IO.popen "#{ @ruby } #{ c['program'] }", 'r+' do |pipe|
73
+ cid = pipe.gets.to_i
74
+ q.push cid if @block
75
+ pipe.read rescue nil
76
+ end
77
+ }
78
+ status = $?
79
+ ensure
80
+ t.kill rescue nil if t
81
+ end
82
+
83
+ if @stdout or @stderr
84
+ open(c['stdout']){|f| relay f => @stdout} if @stdout
85
+ open(c['stderr']){|f| relay f => @stderr} if @stderr
86
+ status
87
+ else
88
+ [status, IO.read(c['stdout']), IO.read(c['stderr'])]
89
+ end
90
+ end
91
+ #--}}}
92
+ end
93
+
94
+ def child_setup tmp
95
+ #--{{{
96
+ stdin = File.expand_path(File.join(tmp, 'stdin'))
97
+ stdout = File.expand_path(File.join(tmp, 'stdout'))
98
+ stderr = File.expand_path(File.join(tmp, 'stderr'))
99
+ program = File.expand_path(File.join(tmp, 'program'))
100
+ config = File.expand_path(File.join(tmp, 'config'))
101
+
102
+ if @stdin
103
+ open(stdin, 'w'){|f| relay @stdin => f}
104
+ else
105
+ FileUtils.touch stdin
106
+ end
107
+ FileUtils.touch stdout
108
+ FileUtils.touch stderr
109
+
110
+ c = {}
111
+ c['argv'] = @argv
112
+ c['env'] = @env
113
+ c['cwd'] = @cwd
114
+ c['stdin'] = stdin
115
+ c['stdout'] = stdout
116
+ c['stderr'] = stderr
117
+ c['program'] = program
118
+ open(config, 'w'){|f| Marshal.dump c, f}
119
+
120
+ open(program, 'w'){|f| f.write child_program(config)}
121
+
122
+ c
123
+ #--}}}
124
+ end
125
+
126
+ def quietly
127
+ #--{{{
128
+ v = $VERBOSE
129
+ $VERBOSE = nil
130
+ yield
131
+ ensure
132
+ $VERBOSE = v
133
+ #--}}}
134
+ end
135
+
136
+ def child_program config
137
+ #--{{{
138
+ <<-program
139
+ begin
140
+ config = Marshal.load(IO.read('#{ config }'))
141
+
142
+ argv = config['argv']
143
+ env = config['env']
144
+ cwd = config['cwd']
145
+ stdin = config['stdin']
146
+ stdout = config['stdout']
147
+ stderr = config['stderr']
148
+
149
+ Dir.chdir cwd if cwd
150
+ env.each{|k,v| ENV[k.to_s] = v.to_s} if env
151
+ rescue Exception => e
152
+ STDERR.warn e
153
+ exit 42
154
+ end
155
+
156
+ STDOUT.puts Process.pid
157
+ STDOUT.flush
158
+
159
+ STDIN.reopen stdin
160
+ STDOUT.reopen stdout
161
+ STDERR.reopen stderr
162
+
163
+ exec *argv
164
+ program
165
+ #--}}}
166
+ end
167
+
168
+ def relay srcdst
169
+ #--{{{
170
+ src, dst, ignored = srcdst.to_a.first
171
+ if src.respond_to? 'read'
172
+ while((buf = src.read(8192))); dst << buf; end
173
+ else
174
+ src.each{|buf| dst << buf}
175
+ end
176
+ #--}}}
177
+ end
178
+
179
+ def tmpdir d = Dir.tmpdir, &b
180
+ #--{{{
181
+ i = -1 and loop{
182
+ tmp = File.join d, "systemu_#{ @host }_#{ @ppid }_#{ @pid }_#{ rand }_#{ i += 1 }"
183
+
184
+ begin
185
+ Dir.mkdir tmp
186
+ rescue Errno::EEXIST
187
+ next
188
+ end
189
+
190
+ scrub = lambda do |t|
191
+ FileUtils.rm_rf t
192
+ scrub = lambda{ 42 }
193
+ end
194
+ at_exit{ scrub[tmp] }
195
+
196
+ break(
197
+ if b
198
+ begin; b[ tmp ]; ensure; scrub[ tmp ]; end
199
+ else
200
+ tmp
201
+ end
202
+ )
203
+ }
204
+ #--}}}
205
+ end
206
+
207
+ def getopts opts = {}
208
+ #--{{{
209
+ lambda do |*args|
210
+ keys, default, ignored = args
211
+ catch('opt') do
212
+ [keys].flatten.each do |key|
213
+ [key, key.to_s, key.to_s.intern].each do |key|
214
+ throw 'opt', opts[key] if opts.has_key?(key)
215
+ end
216
+ end
217
+ default
218
+ end
219
+ end
220
+ #--}}}
221
+ end
222
+ #--}}}
223
+ end
224
+
225
+
226
+ if $0 == __FILE__
227
+ #
228
+ # date
229
+ #
230
+ date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " )
231
+
232
+ status, stdout, stderr = systemu date
233
+ p [status, stdout, stderr]
234
+
235
+ status = systemu date, 1=>(stdout = '')
236
+ p [status, stdout]
237
+
238
+ status = systemu date, 2=>(stderr = '')
239
+ p [status, stderr]
240
+ #
241
+ # sleep
242
+ #
243
+ sleep = %q( ruby -e" p(sleep(1)) " )
244
+ status, stdout, stderr = systemu sleep
245
+ p [status, stdout, stderr]
246
+
247
+ sleep = %q( ruby -e" p(sleep(42)) " )
248
+ status, stdout, stderr = systemu(sleep){|cid| Process.kill 9, cid}
249
+ p [status, stdout, stderr]
250
+ #
251
+ # env
252
+ #
253
+ env = %q( ruby -e" p ENV['A'] " )
254
+ status, stdout, stderr = systemu env, :env => {'A' => 42}
255
+ p [status, stdout, stderr]
256
+ #
257
+ # cwd
258
+ #
259
+ env = %q( ruby -e" p Dir.pwd " )
260
+ status, stdout, stderr = systemu env, :cwd => Dir.tmpdir
261
+ p [status, stdout, stderr]
262
+ end
@@ -0,0 +1,262 @@
1
+ require 'tmpdir'
2
+ require 'socket'
3
+ require 'fileutils'
4
+ require 'rbconfig'
5
+ require 'thread'
6
+
7
+ class Object
8
+ def systemu(*a, &b) SystemUniversal.new(*a, &b).systemu end
9
+ end
10
+
11
+ class SystemUniversal
12
+ #--{{{
13
+ VERSION = '1.0.0'
14
+ def version() VERSION end
15
+ #
16
+ # class methods
17
+ #
18
+
19
+ @host = Socket.gethostname
20
+ @ppid = Process.ppid
21
+ @pid = Process.pid
22
+
23
+ c = ::Config::CONFIG
24
+ ruby = File.join(c['bindir'], c['ruby_install_name']) << c['EXEEXT']
25
+ @ruby = if system('%s -e 42' % ruby)
26
+ ruby
27
+ else
28
+ system('%s -e 42' % 'ruby') ? 'ruby' : warn('no ruby in PATH/CONFIG')
29
+ end
30
+
31
+ class << self
32
+ %w( host ppid pid ruby ).each{|a| attr_accessor a}
33
+ end
34
+
35
+ #
36
+ # instance methods
37
+ #
38
+
39
+ def initialize argv, opts = {}, &block
40
+ #--{{{
41
+ getopt = getopts opts
42
+
43
+ @argv = argv
44
+ @block = block
45
+
46
+ @stdin = getopt[ ['stdin', 'in', '0', 0] ]
47
+ @stdout = getopt[ ['stdout', 'out', '1', 1] ]
48
+ @stderr = getopt[ ['stderr', 'err', '2', 2] ]
49
+ @env = getopt[ 'env' ]
50
+ @cwd = getopt[ 'cwd' ]
51
+
52
+ @host = getopt[ 'host', self.class.host ]
53
+ @ppid = getopt[ 'ppid', self.class.ppid ]
54
+ @pid = getopt[ 'pid', self.class.pid ]
55
+ @ruby = getopt[ 'ruby', self.class.ruby ]
56
+ #--}}}
57
+ end
58
+
59
+ def systemu
60
+ #--{{{
61
+ tmpdir do |tmp|
62
+ c = child_setup tmp
63
+ status = nil
64
+
65
+ if @block
66
+ q = Queue.new
67
+ t = Thread.new{ @block[ q.pop ] }
68
+ end
69
+
70
+ begin
71
+ quietly{
72
+ IO.popen "#{ @ruby } #{ c['program'] }", 'r+' do |pipe|
73
+ cid = pipe.gets.to_i
74
+ q.push cid if @block
75
+ pipe.read rescue nil
76
+ end
77
+ }
78
+ status = $?
79
+ ensure
80
+ t.kill rescue nil if t
81
+ end
82
+
83
+ if @stdout or @stderr
84
+ open(c['stdout']){|f| relay f => @stdout} if @stdout
85
+ open(c['stderr']){|f| relay f => @stderr} if @stderr
86
+ status
87
+ else
88
+ [status, IO.read(c['stdout']), IO.read(c['stderr'])]
89
+ end
90
+ end
91
+ #--}}}
92
+ end
93
+
94
+ def child_setup tmp
95
+ #--{{{
96
+ stdin = File.expand_path(File.join(tmp, 'stdin'))
97
+ stdout = File.expand_path(File.join(tmp, 'stdout'))
98
+ stderr = File.expand_path(File.join(tmp, 'stderr'))
99
+ program = File.expand_path(File.join(tmp, 'program'))
100
+ config = File.expand_path(File.join(tmp, 'config'))
101
+
102
+ if @stdin
103
+ open(stdin, 'w'){|f| relay @stdin => f}
104
+ else
105
+ FileUtils.touch stdin
106
+ end
107
+ FileUtils.touch stdout
108
+ FileUtils.touch stderr
109
+
110
+ c = {}
111
+ c['argv'] = @argv
112
+ c['env'] = @env
113
+ c['cwd'] = @cwd
114
+ c['stdin'] = stdin
115
+ c['stdout'] = stdout
116
+ c['stderr'] = stderr
117
+ c['program'] = program
118
+ open(config, 'w'){|f| Marshal.dump c, f}
119
+
120
+ open(program, 'w'){|f| f.write child_program(config)}
121
+
122
+ c
123
+ #--}}}
124
+ end
125
+
126
+ def quietly
127
+ #--{{{
128
+ v = $VERBOSE
129
+ $VERBOSE = nil
130
+ yield
131
+ ensure
132
+ $VERBOSE = v
133
+ #--}}}
134
+ end
135
+
136
+ def child_program config
137
+ #--{{{
138
+ <<-program
139
+ begin
140
+ config = Marshal.load(IO.read('#{ config }'))
141
+
142
+ argv = config['argv']
143
+ env = config['env']
144
+ cwd = config['cwd']
145
+ stdin = config['stdin']
146
+ stdout = config['stdout']
147
+ stderr = config['stderr']
148
+
149
+ Dir.chdir cwd if cwd
150
+ env.each{|k,v| ENV[k.to_s] = v.to_s} if env
151
+ rescue Exception => e
152
+ STDERR.warn e
153
+ exit 42
154
+ end
155
+
156
+ STDOUT.puts Process.pid
157
+ STDOUT.flush
158
+
159
+ STDIN.reopen stdin
160
+ STDOUT.reopen stdout
161
+ STDERR.reopen stderr
162
+
163
+ exec *argv
164
+ program
165
+ #--}}}
166
+ end
167
+
168
+ def relay srcdst
169
+ #--{{{
170
+ src, dst, ignored = srcdst.to_a.first
171
+ if src.respond_to? 'read'
172
+ while((buf = src.read(8192))); dst << buf; end
173
+ else
174
+ src.each{|buf| dst << buf}
175
+ end
176
+ #--}}}
177
+ end
178
+
179
+ def tmpdir d = Dir.tmpdir, &b
180
+ #--{{{
181
+ i = -1 and loop{
182
+ tmp = File.join d, "systemu_#{ @host }_#{ @ppid }_#{ @pid }_#{ rand }_#{ i += 1 }"
183
+
184
+ begin
185
+ Dir.mkdir tmp
186
+ rescue Errno::EEXIST
187
+ next
188
+ end
189
+
190
+ scrub = lambda do |t|
191
+ FileUtils.rm_rf t
192
+ scrub = lambda{ 42 }
193
+ end
194
+ at_exit{ scrub[tmp] }
195
+
196
+ break(
197
+ if b
198
+ begin; b[ tmp ]; ensure; scrub[ tmp ]; end
199
+ else
200
+ tmp
201
+ end
202
+ )
203
+ }
204
+ #--}}}
205
+ end
206
+
207
+ def getopts opts = {}
208
+ #--{{{
209
+ lambda do |*args|
210
+ keys, default, ignored = args
211
+ catch('opt') do
212
+ [keys].flatten.each do |key|
213
+ [key, key.to_s, key.to_s.intern].each do |key|
214
+ throw 'opt', opts[key] if opts.has_key?(key)
215
+ end
216
+ end
217
+ default
218
+ end
219
+ end
220
+ #--}}}
221
+ end
222
+ #--}}}
223
+ end
224
+
225
+
226
+ if $0 == __FILE__
227
+ #
228
+ # date
229
+ #
230
+ date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " )
231
+
232
+ status, stdout, stderr = systemu date
233
+ p [status, stdout, stderr]
234
+
235
+ status = systemu date, 1=>(stdout = '')
236
+ p [status, stdout]
237
+
238
+ status = systemu date, 2=>(stderr = '')
239
+ p [status, stderr]
240
+ #
241
+ # sleep
242
+ #
243
+ sleep = %q( ruby -e" p(sleep(1)) " )
244
+ status, stdout, stderr = systemu sleep
245
+ p [status, stdout, stderr]
246
+
247
+ sleep = %q( ruby -e" p(sleep(42)) " )
248
+ status, stdout, stderr = systemu(sleep){|cid| Process.kill 9, cid}
249
+ p [status, stdout, stderr]
250
+ #
251
+ # env
252
+ #
253
+ env = %q( ruby -e" p ENV['A'] " )
254
+ status, stdout, stderr = systemu env, :env => {'A' => 42}
255
+ p [status, stdout, stderr]
256
+ #
257
+ # cwd
258
+ #
259
+ env = %q( ruby -e" p Dir.pwd " )
260
+ status, stdout, stderr = systemu env, :cwd => Dir.tmpdir
261
+ p [status, stdout, stderr]
262
+ end
@@ -0,0 +1,11 @@
1
+ #
2
+ # systemu can be used on any platform to return status, stdout, and stderr of
3
+ # any command. unlike other methods like open3/popen4 there is zero danger of
4
+ # full pipes or threading issues hanging your process or subprocess.
5
+ #
6
+ require 'systemu'
7
+
8
+ date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " )
9
+
10
+ status, stdout, stderr = systemu date
11
+ p [ status, stdout, stderr ]
@@ -0,0 +1,12 @@
1
+ #
2
+ # quite a few keys can be passed to the command to alter it's behaviour. if
3
+ # either stdout or stderr is supplied those objects should respond_to? '<<'
4
+ # and only status will be returned
5
+ #
6
+ require 'systemu'
7
+
8
+ date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " )
9
+
10
+ stdout, stderr = '', ''
11
+ status = systemu date, 'stdout' => stdout, 'stderr' => stderr
12
+ p [ status, stdout, stderr ]
@@ -0,0 +1,10 @@
1
+ #
2
+ # of course stdin can be supplied too. synonyms for 'stdin' include '0' and
3
+ # 0. the other stdio streams have similar shortcuts
4
+ #
5
+ require 'systemu'
6
+
7
+ cat = %q( ruby -e" ARGF.each{|line| puts line} " )
8
+
9
+ status = systemu cat, 0=>'the stdin for cat', 1=>stdout=''
10
+ puts stdout
@@ -0,0 +1,11 @@
1
+ #
2
+ # the cwd can be supplied
3
+ #
4
+ require 'systemu'
5
+ require 'tmpdir'
6
+
7
+ pwd = %q( ruby -e" STDERR.puts Dir.pwd " )
8
+
9
+ status = systemu pwd, 2=>(stderr=''), :cwd=>Dir.tmpdir
10
+ puts stderr
11
+
@@ -0,0 +1,9 @@
1
+ #
2
+ # any environment vars specified are merged into the child's environment
3
+ #
4
+ require 'systemu'
5
+
6
+ env = %q( ruby -r yaml -e" puts ENV[ 'answer' ] " )
7
+
8
+ status = systemu env, 1=>stdout='', 'env'=>{ 'answer' => 0b101010 }
9
+ puts stdout
@@ -0,0 +1,18 @@
1
+ #
2
+ # if a block is specified then it is passed the child pid and run in a
3
+ # background thread. note that this thread will __not__ be blocked during the
4
+ # execution of the command so it may do useful work such as killing the child
5
+ # if execution time passes a certain threshold
6
+ #
7
+ require 'systemu'
8
+
9
+ looper = %q( ruby -e" loop{ STDERR.puts Time.now.to_i; sleep 1 } " )
10
+
11
+ status, stdout, stderr =
12
+ systemu looper do |cid|
13
+ sleep 3
14
+ Process.kill 9, cid
15
+ end
16
+
17
+ p [ status, stdout, stderr ]
18
+
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.11
3
+ specification_version: 1
4
+ name: systemu
5
+ version: !ruby/object:Gem::Version
6
+ version: 1.0.0
7
+ date: 2006-11-03 00:00:00.000000 -07:00
8
+ summary: systemu
9
+ require_paths:
10
+ - lib
11
+ email: ara.t.howard@noaa.gov
12
+ homepage: http://codeforpeople.com/lib/ruby/systemu/
13
+ rubyforge_project:
14
+ description:
15
+ autorequire: systemu
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: false
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ -
22
+ - ">"
23
+ - !ruby/object:Gem::Version
24
+ version: 0.0.0
25
+ version:
26
+ platform: ruby
27
+ signing_key:
28
+ cert_chain:
29
+ authors:
30
+ - Ara T. Howard
31
+ files:
32
+ - lib
33
+ - samples
34
+ - gen_readme.rb
35
+ - gemspec.rb
36
+ - install.rb
37
+ - README
38
+ - README.tmpl
39
+ - lib/systemu.rb
40
+ - lib/systemu-1.0.0.rb
41
+ - samples/a.rb
42
+ - samples/b.rb
43
+ - samples/c.rb
44
+ - samples/d.rb
45
+ - samples/e.rb
46
+ - samples/f.rb
47
+ test_files: []
48
+ rdoc_options: []
49
+ extra_rdoc_files: []
50
+ executables: []
51
+ extensions: []
52
+ requirements: []
53
+ dependencies: []