open4 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +3 -0
- data/Rakefile +175 -29
- data/lib/open4.rb +6 -28
- data/open4.gemspec +25 -7
- data/samples/jesse-caldwell.rb +131 -0
- metadata +13 -12
data/LICENSE
ADDED
data/Rakefile
CHANGED
@@ -1,18 +1,66 @@
|
|
1
|
-
|
2
1
|
This.rubyforge_project = 'codeforpeople'
|
3
2
|
This.author = "Ara T. Howard"
|
4
3
|
This.email = "ara.t.howard@gmail.com"
|
5
|
-
This.homepage = "
|
4
|
+
This.homepage = "https://github.com/ahoward/#{ This.lib }"
|
6
5
|
|
7
6
|
|
8
7
|
task :default do
|
9
|
-
puts(Rake::Task.tasks.map{|task| task.name} - ['default'])
|
8
|
+
puts((Rake::Task.tasks.map{|task| task.name.gsub(/::/,':')} - ['default']).sort)
|
9
|
+
end
|
10
|
+
|
11
|
+
task :test do
|
12
|
+
run_tests!
|
13
|
+
end
|
14
|
+
|
15
|
+
namespace :test do
|
16
|
+
task(:unit){ run_tests!(:unit) }
|
17
|
+
task(:functional){ run_tests!(:functional) }
|
18
|
+
task(:integration){ run_tests!(:integration) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def run_tests!(which = nil)
|
22
|
+
which ||= '**'
|
23
|
+
test_dir = File.join(This.dir, "test")
|
24
|
+
test_glob ||= File.join(test_dir, "#{ which }/**_test.rb")
|
25
|
+
test_rbs = Dir.glob(test_glob).sort
|
26
|
+
|
27
|
+
div = ('=' * 119)
|
28
|
+
line = ('-' * 119)
|
29
|
+
|
30
|
+
test_rbs.each_with_index do |test_rb, index|
|
31
|
+
testno = index + 1
|
32
|
+
command = "#{ This.ruby } -I ./lib -I ./test/lib #{ test_rb }"
|
33
|
+
|
34
|
+
puts
|
35
|
+
say(div, :color => :cyan, :bold => true)
|
36
|
+
say("@#{ testno } => ", :bold => true, :method => :print)
|
37
|
+
say(command, :color => :cyan, :bold => true)
|
38
|
+
say(line, :color => :cyan, :bold => true)
|
39
|
+
|
40
|
+
system(command)
|
41
|
+
|
42
|
+
say(line, :color => :cyan, :bold => true)
|
43
|
+
|
44
|
+
status = $?.exitstatus
|
45
|
+
|
46
|
+
if status.zero?
|
47
|
+
say("@#{ testno } <= ", :bold => true, :color => :white, :method => :print)
|
48
|
+
say("SUCCESS", :color => :green, :bold => true)
|
49
|
+
else
|
50
|
+
say("@#{ testno } <= ", :bold => true, :color => :white, :method => :print)
|
51
|
+
say("FAILURE", :color => :red, :bold => true)
|
52
|
+
end
|
53
|
+
say(line, :color => :cyan, :bold => true)
|
54
|
+
|
55
|
+
exit(status) unless status.zero?
|
56
|
+
end
|
10
57
|
end
|
11
58
|
|
12
59
|
|
13
60
|
task :gemspec do
|
14
|
-
ignore_extensions = 'git', 'svn', 'tmp', /sw./, 'bak', 'gem'
|
15
|
-
ignore_directories = 'pkg'
|
61
|
+
ignore_extensions = ['git', 'svn', 'tmp', /sw./, 'bak', 'gem']
|
62
|
+
ignore_directories = ['pkg']
|
63
|
+
ignore_files = ['test/log']
|
16
64
|
|
17
65
|
shiteless =
|
18
66
|
lambda do |list|
|
@@ -26,17 +74,26 @@ task :gemspec do
|
|
26
74
|
dirname = File.expand_path(entry)
|
27
75
|
ignore_directories.any?{|dir| File.expand_path(dir) == dirname}
|
28
76
|
end
|
77
|
+
list.delete_if do |entry|
|
78
|
+
next unless test(?f, entry)
|
79
|
+
filename = File.expand_path(entry)
|
80
|
+
ignore_files.any?{|file| File.expand_path(file) == filename}
|
81
|
+
end
|
29
82
|
end
|
30
83
|
|
31
84
|
lib = This.lib
|
85
|
+
object = This.object
|
32
86
|
version = This.version
|
33
87
|
files = shiteless[Dir::glob("**/**")]
|
34
88
|
executables = shiteless[Dir::glob("bin/*")].map{|exe| File.basename(exe)}
|
35
|
-
has_rdoc = true #File.exist?('doc')
|
89
|
+
#has_rdoc = true #File.exist?('doc')
|
36
90
|
test_files = "test/#{ lib }.rb" if File.file?("test/#{ lib }.rb")
|
91
|
+
summary = object.respond_to?(:summary) ? object.summary : "summary: #{ lib } kicks the ass"
|
92
|
+
description = object.respond_to?(:description) ? object.description : "description: #{ lib } kicks the ass"
|
37
93
|
|
38
|
-
|
39
|
-
|
94
|
+
if This.extensions.nil?
|
95
|
+
This.extensions = []
|
96
|
+
extensions = This.extensions
|
40
97
|
%w( Makefile configure extconf.rb ).each do |ext|
|
41
98
|
extensions << ext if File.exists?(ext)
|
42
99
|
end
|
@@ -57,18 +114,17 @@ task :gemspec do
|
|
57
114
|
spec.version = #{ version.inspect }
|
58
115
|
spec.platform = Gem::Platform::RUBY
|
59
116
|
spec.summary = #{ lib.inspect }
|
117
|
+
spec.description = #{ description.inspect }
|
60
118
|
|
61
|
-
spec.
|
62
|
-
|
63
|
-
spec.files = #{ files.inspect }
|
119
|
+
spec.files =\n#{ files.sort.pretty_inspect }
|
64
120
|
spec.executables = #{ executables.inspect }
|
65
121
|
|
66
122
|
spec.require_path = "lib"
|
67
123
|
|
68
|
-
spec.has_rdoc = #{ has_rdoc.inspect }
|
69
124
|
spec.test_files = #{ test_files.inspect }
|
70
|
-
|
71
|
-
|
125
|
+
|
126
|
+
### spec.add_dependency 'lib', '>= version'
|
127
|
+
#### spec.add_dependency 'map'
|
72
128
|
|
73
129
|
spec.extensions.push(*#{ extensions.inspect })
|
74
130
|
|
@@ -81,22 +137,23 @@ task :gemspec do
|
|
81
137
|
}
|
82
138
|
end
|
83
139
|
|
84
|
-
|
85
|
-
|
140
|
+
Fu.mkdir_p(This.pkgdir)
|
141
|
+
gemspec = "#{ lib }.gemspec"
|
142
|
+
open(gemspec, "w"){|fd| fd.puts(template)}
|
143
|
+
This.gemspec = gemspec
|
86
144
|
end
|
87
145
|
|
88
146
|
task :gem => [:clean, :gemspec] do
|
89
|
-
Fu.mkdir_p
|
147
|
+
Fu.mkdir_p(This.pkgdir)
|
90
148
|
before = Dir['*.gem']
|
91
149
|
cmd = "gem build #{ This.gemspec }"
|
92
150
|
`#{ cmd }`
|
93
151
|
after = Dir['*.gem']
|
94
152
|
gem = ((after - before).first || after.first) or abort('no gem!')
|
95
|
-
Fu.mv
|
96
|
-
This.gem = File.basename(gem)
|
153
|
+
Fu.mv(gem, This.pkgdir)
|
154
|
+
This.gem = File.join(This.pkgdir, File.basename(gem))
|
97
155
|
end
|
98
156
|
|
99
|
-
|
100
157
|
task :readme do
|
101
158
|
samples = ''
|
102
159
|
prompt = '~ > '
|
@@ -113,7 +170,7 @@ task :readme do
|
|
113
170
|
cmd = "ruby #{ sample }"
|
114
171
|
samples << Util.indent(prompt + cmd, 2) << "\n\n"
|
115
172
|
|
116
|
-
cmd = "ruby -e'STDOUT.sync=true; exec %(ruby -
|
173
|
+
cmd = "ruby -e'STDOUT.sync=true; exec %(ruby -I ./lib #{ sample })'"
|
117
174
|
samples << Util.indent(`#{ cmd } 2>&1`, 4) << "\n"
|
118
175
|
end
|
119
176
|
|
@@ -150,9 +207,18 @@ task :release => [:clean, :gemspec, :gem] do
|
|
150
207
|
gems = Dir[File.join(This.pkgdir, '*.gem')].flatten
|
151
208
|
raise "which one? : #{ gems.inspect }" if gems.size > 1
|
152
209
|
raise "no gems?" if gems.size < 1
|
153
|
-
|
210
|
+
|
211
|
+
cmd = "gem push #{ This.gem }"
|
212
|
+
puts cmd
|
213
|
+
puts
|
214
|
+
system(cmd)
|
215
|
+
abort("cmd(#{ cmd }) failed with (#{ $?.inspect })") unless $?.exitstatus.zero?
|
216
|
+
|
217
|
+
cmd = "rubyforge login && rubyforge add_release #{ This.rubyforge_project } #{ This.lib } #{ This.version } #{ This.gem }"
|
154
218
|
puts cmd
|
155
|
-
|
219
|
+
puts
|
220
|
+
system(cmd)
|
221
|
+
abort("cmd(#{ cmd }) failed with (#{ $?.inspect })") unless $?.exitstatus.zero?
|
156
222
|
end
|
157
223
|
|
158
224
|
|
@@ -160,37 +226,63 @@ end
|
|
160
226
|
|
161
227
|
|
162
228
|
BEGIN {
|
229
|
+
# support for this rakefile
|
230
|
+
#
|
163
231
|
$VERBOSE = nil
|
164
232
|
|
165
233
|
require 'ostruct'
|
166
234
|
require 'erb'
|
167
235
|
require 'fileutils'
|
236
|
+
require 'rbconfig'
|
237
|
+
require 'pp'
|
168
238
|
|
239
|
+
# fu shortcut
|
240
|
+
#
|
169
241
|
Fu = FileUtils
|
170
242
|
|
243
|
+
# cache a bunch of stuff about this rakefile/environment
|
244
|
+
#
|
171
245
|
This = OpenStruct.new
|
172
246
|
|
173
247
|
This.file = File.expand_path(__FILE__)
|
174
248
|
This.dir = File.dirname(This.file)
|
175
249
|
This.pkgdir = File.join(This.dir, 'pkg')
|
176
250
|
|
251
|
+
# grok lib
|
252
|
+
#
|
177
253
|
lib = ENV['LIB']
|
178
254
|
unless lib
|
179
|
-
lib = File.basename(Dir.pwd)
|
255
|
+
lib = File.basename(Dir.pwd).sub(/[-].*$/, '')
|
180
256
|
end
|
181
257
|
This.lib = lib
|
182
258
|
|
259
|
+
# grok version
|
260
|
+
#
|
183
261
|
version = ENV['VERSION']
|
184
262
|
unless version
|
185
|
-
|
186
|
-
|
187
|
-
|
263
|
+
require "./lib/#{ This.lib }"
|
264
|
+
This.name = lib.capitalize
|
265
|
+
This.object = eval(This.name)
|
266
|
+
version = This.object.send(:version)
|
188
267
|
end
|
189
268
|
This.version = version
|
190
269
|
|
270
|
+
# we need to know the name of the lib an it's version
|
271
|
+
#
|
191
272
|
abort('no lib') unless This.lib
|
192
273
|
abort('no version') unless This.version
|
193
274
|
|
275
|
+
# discover full path to this ruby executable
|
276
|
+
#
|
277
|
+
c = Config::CONFIG
|
278
|
+
bindir = c["bindir"] || c['BINDIR']
|
279
|
+
ruby_install_name = c['ruby_install_name'] || c['RUBY_INSTALL_NAME'] || 'ruby'
|
280
|
+
ruby_ext = c['EXEEXT'] || ''
|
281
|
+
ruby = File.join(bindir, (ruby_install_name + ruby_ext))
|
282
|
+
This.ruby = ruby
|
283
|
+
|
284
|
+
# some utils
|
285
|
+
#
|
194
286
|
module Util
|
195
287
|
def indent(s, n = 2)
|
196
288
|
s = unindent(s)
|
@@ -200,7 +292,7 @@ BEGIN {
|
|
200
292
|
|
201
293
|
def unindent(s)
|
202
294
|
indent = nil
|
203
|
-
s.
|
295
|
+
s.each_line do |line|
|
204
296
|
next if line =~ %r/^\s*$/
|
205
297
|
indent = line[%r/^\s*/] and break
|
206
298
|
end
|
@@ -209,17 +301,71 @@ BEGIN {
|
|
209
301
|
extend self
|
210
302
|
end
|
211
303
|
|
304
|
+
# template support
|
305
|
+
#
|
212
306
|
class Template
|
213
307
|
def initialize(&block)
|
214
308
|
@block = block
|
215
309
|
@template = block.call.to_s
|
216
310
|
end
|
217
311
|
def expand(b=nil)
|
218
|
-
ERB.new(Util.unindent(@template)).result(b||@block)
|
312
|
+
ERB.new(Util.unindent(@template)).result((b||@block).binding)
|
219
313
|
end
|
220
314
|
alias_method 'to_s', 'expand'
|
221
315
|
end
|
222
316
|
def Template(*args, &block) Template.new(*args, &block) end
|
223
317
|
|
318
|
+
# colored console output support
|
319
|
+
#
|
320
|
+
This.ansi = {
|
321
|
+
:clear => "\e[0m",
|
322
|
+
:reset => "\e[0m",
|
323
|
+
:erase_line => "\e[K",
|
324
|
+
:erase_char => "\e[P",
|
325
|
+
:bold => "\e[1m",
|
326
|
+
:dark => "\e[2m",
|
327
|
+
:underline => "\e[4m",
|
328
|
+
:underscore => "\e[4m",
|
329
|
+
:blink => "\e[5m",
|
330
|
+
:reverse => "\e[7m",
|
331
|
+
:concealed => "\e[8m",
|
332
|
+
:black => "\e[30m",
|
333
|
+
:red => "\e[31m",
|
334
|
+
:green => "\e[32m",
|
335
|
+
:yellow => "\e[33m",
|
336
|
+
:blue => "\e[34m",
|
337
|
+
:magenta => "\e[35m",
|
338
|
+
:cyan => "\e[36m",
|
339
|
+
:white => "\e[37m",
|
340
|
+
:on_black => "\e[40m",
|
341
|
+
:on_red => "\e[41m",
|
342
|
+
:on_green => "\e[42m",
|
343
|
+
:on_yellow => "\e[43m",
|
344
|
+
:on_blue => "\e[44m",
|
345
|
+
:on_magenta => "\e[45m",
|
346
|
+
:on_cyan => "\e[46m",
|
347
|
+
:on_white => "\e[47m"
|
348
|
+
}
|
349
|
+
def say(phrase, *args)
|
350
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
351
|
+
options[:color] = args.shift.to_s.to_sym unless args.empty?
|
352
|
+
keys = options.keys
|
353
|
+
keys.each{|key| options[key.to_s.to_sym] = options.delete(key)}
|
354
|
+
|
355
|
+
color = options[:color]
|
356
|
+
bold = options.has_key?(:bold)
|
357
|
+
|
358
|
+
parts = [phrase]
|
359
|
+
parts.unshift(This.ansi[color]) if color
|
360
|
+
parts.unshift(This.ansi[:bold]) if bold
|
361
|
+
parts.push(This.ansi[:clear]) if parts.size > 1
|
362
|
+
|
363
|
+
method = options[:method] || :puts
|
364
|
+
|
365
|
+
Kernel.send(method, parts.join)
|
366
|
+
end
|
367
|
+
|
368
|
+
# always run out of the project dir
|
369
|
+
#
|
224
370
|
Dir.chdir(This.dir)
|
225
371
|
}
|
data/lib/open4.rb
CHANGED
@@ -4,14 +4,12 @@ require 'timeout'
|
|
4
4
|
require 'thread'
|
5
5
|
|
6
6
|
module Open4
|
7
|
-
|
8
|
-
VERSION = '1.0.1'
|
7
|
+
VERSION = '1.1.0'
|
9
8
|
def self.version() VERSION end
|
10
9
|
|
11
10
|
class Error < ::StandardError; end
|
12
11
|
|
13
12
|
def popen4(*cmd, &b)
|
14
|
-
#--{{{
|
15
13
|
pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
|
16
14
|
|
17
15
|
verbose = $VERBOSE
|
@@ -74,14 +72,12 @@ module Open4
|
|
74
72
|
else
|
75
73
|
[cid, pw.last, pr.first, pe.first]
|
76
74
|
end
|
77
|
-
#--}}}
|
78
75
|
end
|
79
76
|
alias open4 popen4
|
80
77
|
module_function :popen4
|
81
78
|
module_function :open4
|
82
79
|
|
83
80
|
class SpawnError < Error
|
84
|
-
#--{{{
|
85
81
|
attr 'cmd'
|
86
82
|
attr 'status'
|
87
83
|
attr 'signals'
|
@@ -98,11 +94,9 @@ module Open4
|
|
98
94
|
sigs = @signals.map{|k,v| "#{ k }:#{ v.inspect }"}.join(' ')
|
99
95
|
super "cmd <#{ cmd }> failed with status <#{ exitstatus.inspect }> signals <#{ sigs }>"
|
100
96
|
end
|
101
|
-
#--}}}
|
102
97
|
end
|
103
98
|
|
104
99
|
class ThreadEnsemble
|
105
|
-
#--{{{
|
106
100
|
attr 'threads'
|
107
101
|
|
108
102
|
def initialize cid
|
@@ -154,18 +148,14 @@ module Open4
|
|
154
148
|
def all_done
|
155
149
|
@threads.size.times{ @done.pop }
|
156
150
|
end
|
157
|
-
#--}}}
|
158
151
|
end
|
159
152
|
|
160
153
|
def to timeout = nil
|
161
|
-
#--{{{
|
162
154
|
Timeout.timeout(timeout){ yield }
|
163
|
-
#--}}}
|
164
155
|
end
|
165
156
|
module_function :to
|
166
157
|
|
167
158
|
def new_thread *a, &b
|
168
|
-
#--{{{
|
169
159
|
cur = Thread.current
|
170
160
|
Thread.new(*a) do |*a|
|
171
161
|
begin
|
@@ -174,34 +164,32 @@ module Open4
|
|
174
164
|
cur.raise e
|
175
165
|
end
|
176
166
|
end
|
177
|
-
#--}}}
|
178
167
|
end
|
179
168
|
module_function :new_thread
|
180
169
|
|
181
170
|
def getopts opts = {}
|
182
|
-
#--{{{
|
183
171
|
lambda do |*args|
|
184
172
|
keys, default, ignored = args
|
185
|
-
catch(
|
173
|
+
catch(:opt) do
|
186
174
|
[keys].flatten.each do |key|
|
187
175
|
[key, key.to_s, key.to_s.intern].each do |key|
|
188
|
-
throw
|
176
|
+
throw :opt, opts[key] if opts.has_key?(key)
|
189
177
|
end
|
190
178
|
end
|
191
179
|
default
|
192
180
|
end
|
193
181
|
end
|
194
|
-
#--}}}
|
195
182
|
end
|
196
183
|
module_function :getopts
|
197
184
|
|
198
185
|
def relay src, dst = nil, t = nil
|
199
|
-
#--{{{
|
200
186
|
send_dst =
|
201
187
|
if dst.respond_to?(:call)
|
202
188
|
lambda{|buf| dst.call(buf)}
|
189
|
+
elsif dst.respond_to?(:<<)
|
190
|
+
lambda{|buf| dst << buf }
|
203
191
|
else
|
204
|
-
lambda{|buf|
|
192
|
+
lambda{|buf| buf }
|
205
193
|
end
|
206
194
|
|
207
195
|
unless src.nil?
|
@@ -242,12 +230,10 @@ module Open4
|
|
242
230
|
send_dst[buf]
|
243
231
|
end
|
244
232
|
end
|
245
|
-
#--}}}
|
246
233
|
end
|
247
234
|
module_function :relay
|
248
235
|
|
249
236
|
def spawn arg, *argv
|
250
|
-
#--{{{
|
251
237
|
argv.unshift(arg)
|
252
238
|
opts = ((argv.size > 1 and Hash === argv.last) ? argv.pop : {})
|
253
239
|
argv.flatten!
|
@@ -323,7 +309,6 @@ module Open4
|
|
323
309
|
(ignore_exit_failure or (status.nil? and ignore_exec_failure) or exitstatus.include?(status.exitstatus))
|
324
310
|
|
325
311
|
status
|
326
|
-
#--}}}
|
327
312
|
end
|
328
313
|
module_function :spawn
|
329
314
|
|
@@ -334,7 +319,6 @@ module Open4
|
|
334
319
|
module_function :chdir
|
335
320
|
|
336
321
|
def background arg, *argv
|
337
|
-
#--{{{
|
338
322
|
require 'thread'
|
339
323
|
q = Queue.new
|
340
324
|
opts = { 'pid' => q, :pid => q }
|
@@ -352,14 +336,12 @@ module Open4
|
|
352
336
|
define_method(:exitstatus){ @exitstatus ||= spawn_status.exitstatus }
|
353
337
|
}
|
354
338
|
thread
|
355
|
-
#--}}}
|
356
339
|
end
|
357
340
|
alias bg background
|
358
341
|
module_function :background
|
359
342
|
module_function :bg
|
360
343
|
|
361
344
|
def maim pid, opts = {}
|
362
|
-
#--{{{
|
363
345
|
getopt = getopts opts
|
364
346
|
sigs = getopt[ 'signals', %w(SIGTERM SIGQUIT SIGKILL) ]
|
365
347
|
suspend = getopt[ 'suspend', 4 ]
|
@@ -377,12 +359,10 @@ module Open4
|
|
377
359
|
return true unless alive? pid
|
378
360
|
end
|
379
361
|
return(not alive?(pid))
|
380
|
-
#--}}}
|
381
362
|
end
|
382
363
|
module_function :maim
|
383
364
|
|
384
365
|
def alive pid
|
385
|
-
#--{{{
|
386
366
|
pid = Integer pid
|
387
367
|
begin
|
388
368
|
Process.kill 0, pid
|
@@ -390,12 +370,10 @@ module Open4
|
|
390
370
|
rescue Errno::ESRCH
|
391
371
|
false
|
392
372
|
end
|
393
|
-
#--}}}
|
394
373
|
end
|
395
374
|
alias alive? alive
|
396
375
|
module_function :alive
|
397
376
|
module_function :'alive?'
|
398
|
-
#--}}}
|
399
377
|
end
|
400
378
|
|
401
379
|
def open4(*cmd, &b) cmd.size == 0 ? Open4 : Open4::popen4(*cmd, &b) end
|
data/open4.gemspec
CHANGED
@@ -3,26 +3,44 @@
|
|
3
3
|
|
4
4
|
Gem::Specification::new do |spec|
|
5
5
|
spec.name = "open4"
|
6
|
-
spec.version = "1.0
|
6
|
+
spec.version = "1.1.0"
|
7
7
|
spec.platform = Gem::Platform::RUBY
|
8
8
|
spec.summary = "open4"
|
9
|
+
spec.description = "description: open4 kicks the ass"
|
9
10
|
|
10
|
-
spec.
|
11
|
+
spec.files =
|
12
|
+
["LICENSE",
|
13
|
+
"README",
|
14
|
+
"README.erb",
|
15
|
+
"Rakefile",
|
16
|
+
"lib",
|
17
|
+
"lib/open4.rb",
|
18
|
+
"open4.gemspec",
|
19
|
+
"samples",
|
20
|
+
"samples/bg.rb",
|
21
|
+
"samples/block.rb",
|
22
|
+
"samples/exception.rb",
|
23
|
+
"samples/jesse-caldwell.rb",
|
24
|
+
"samples/simple.rb",
|
25
|
+
"samples/spawn.rb",
|
26
|
+
"samples/stdin_timeout.rb",
|
27
|
+
"samples/timeout.rb",
|
28
|
+
"white_box",
|
29
|
+
"white_box/leak.rb"]
|
11
30
|
|
12
|
-
spec.files = ["lib", "lib/open4.rb", "open4.gemspec", "Rakefile", "README", "README.erb", "samples", "samples/bg.rb", "samples/block.rb", "samples/exception.rb", "samples/simple.rb", "samples/spawn.rb", "samples/stdin_timeout.rb", "samples/timeout.rb", "white_box", "white_box/leak.rb"]
|
13
31
|
spec.executables = []
|
14
32
|
|
15
33
|
spec.require_path = "lib"
|
16
34
|
|
17
|
-
spec.has_rdoc = true
|
18
35
|
spec.test_files = nil
|
19
|
-
|
20
|
-
|
36
|
+
|
37
|
+
### spec.add_dependency 'lib', '>= version'
|
38
|
+
#### spec.add_dependency 'map'
|
21
39
|
|
22
40
|
spec.extensions.push(*[])
|
23
41
|
|
24
42
|
spec.rubyforge_project = "codeforpeople"
|
25
43
|
spec.author = "Ara T. Howard"
|
26
44
|
spec.email = "ara.t.howard@gmail.com"
|
27
|
-
spec.homepage = "
|
45
|
+
spec.homepage = "https://github.com/ahoward/open4"
|
28
46
|
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'open4'
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
# define a function we can call later. the function will take two
|
7
|
+
# arguments:
|
8
|
+
# command which we will run via open4 and be able to
|
9
|
+
# send stdin as well as collect the pid, stderr and stdout.
|
10
|
+
# input optional data string to send to command on stdin
|
11
|
+
#
|
12
|
+
# this returns a hash of the command's pid, stderr, stdout, and status.
|
13
|
+
def run_cmd(command, input = nil)
|
14
|
+
|
15
|
+
# we will use open4 in block form, which means that the variables
|
16
|
+
# used inside the block will not be available once open4 has
|
17
|
+
# finished. as long as variables are declared outside of the
|
18
|
+
# block, they can be set inside the block and are available after
|
19
|
+
# the block has finished.
|
20
|
+
err = out = procid = status = verbose = nil
|
21
|
+
|
22
|
+
# using a begin so we can use rescue later on.
|
23
|
+
begin
|
24
|
+
|
25
|
+
# run our command with open4 in block form.
|
26
|
+
stat = Open4::popen4(command) do |pid, stdin, stdout, stderr|
|
27
|
+
|
28
|
+
# the default behavior of ruby is to internally buffer I/O
|
29
|
+
# ports when they are opened. open4 may not detect that stderr
|
30
|
+
# and/or stdout has closed because ruby is helpfully buffering
|
31
|
+
# the pipe for us. if open4 hangs, try uncommenting the next
|
32
|
+
# two lines.
|
33
|
+
# stderr.sync = true
|
34
|
+
# stdout.sync = true
|
35
|
+
|
36
|
+
# set procid to pid so we can see it outside of the block.
|
37
|
+
procid = pid
|
38
|
+
|
39
|
+
# if you want to use stdin, talk to stdin here. i tried it
|
40
|
+
# with bc. generally i only need to capture output, not
|
41
|
+
# interact with commands i'm running.
|
42
|
+
stdin.puts input if input
|
43
|
+
|
44
|
+
# stdin is opened write only. you'll raise an exception if
|
45
|
+
# you try to read anything from it. here you can try to read
|
46
|
+
# the first character from stdin.
|
47
|
+
# stdin.gets(1)
|
48
|
+
|
49
|
+
# now close stdin.
|
50
|
+
stdin.close
|
51
|
+
|
52
|
+
# make stderr and stdout available outside the block as well.
|
53
|
+
# removing the read will return pointers to objects rather
|
54
|
+
# than the data that the objects contain.
|
55
|
+
out = stdout.read
|
56
|
+
err = stderr.read
|
57
|
+
|
58
|
+
# as stdin is write-only, stderr and stdout are read only.
|
59
|
+
# you'll raise an exception if you try to write to either.
|
60
|
+
# stderr.puts 'building appears to be on fire'
|
61
|
+
|
62
|
+
# end of open4 block. pid, stdin, stdout and stderr are no
|
63
|
+
# longer accessible.
|
64
|
+
end
|
65
|
+
|
66
|
+
# now outside of the open4 block, we can get the exit status
|
67
|
+
# of our command by calling stat.exitstatus.
|
68
|
+
status = stat.exitstatus
|
69
|
+
|
70
|
+
# our function returns status from a command. however, if you
|
71
|
+
# tell the function to run a command that does not exist, ruby
|
72
|
+
# will raise an exception. we will trap that exception here, make
|
73
|
+
# up a non-zero exit status, convert the ruby error to a string,
|
74
|
+
# and populate err with it.
|
75
|
+
rescue Errno::ENOENT => stderr
|
76
|
+
status = 1
|
77
|
+
err = stderr.to_s
|
78
|
+
|
79
|
+
# handle null commands gracefully
|
80
|
+
rescue TypeError => stderr
|
81
|
+
status = 2
|
82
|
+
err = 'Can\'t execute null command.'
|
83
|
+
|
84
|
+
# done calling and/or rescuing open4.
|
85
|
+
end
|
86
|
+
|
87
|
+
# uncomment to make function print output.
|
88
|
+
verbose = true
|
89
|
+
|
90
|
+
# print the values if verbose is not nil.
|
91
|
+
print "\n============================================================" if verbose
|
92
|
+
print "\ncommand: #{ command }" if verbose
|
93
|
+
print "\ninput : \n\n#{ input }\n" if (verbose and input)
|
94
|
+
print "\npid : #{ procid }" if verbose
|
95
|
+
print "\nstatus : #{ status }" if verbose
|
96
|
+
print "\nstdout : #{ out }\n" if verbose
|
97
|
+
print "\nstderr : #{ err }\n" if verbose
|
98
|
+
print "============================================================\n" if verbose
|
99
|
+
|
100
|
+
# now that (we think) we have handled everything, return a hash
|
101
|
+
# with the process id, standard error, standard output, and the
|
102
|
+
# exit status.
|
103
|
+
return {
|
104
|
+
:pid => procid, # integer
|
105
|
+
:stderr => err, # string
|
106
|
+
:stdout => out, # string
|
107
|
+
:status => status, # integer
|
108
|
+
}
|
109
|
+
|
110
|
+
# return terminates function. code here will not run!
|
111
|
+
print 'this will never show up.'
|
112
|
+
|
113
|
+
# end of run_cmd function.
|
114
|
+
end
|
115
|
+
|
116
|
+
# this will raise an exception which our function will trap,
|
117
|
+
# complaining that the command does not exist.
|
118
|
+
cmd = run_cmd('/bin/does/not/exist')
|
119
|
+
|
120
|
+
# something that will produce a fair amount of output. you do have
|
121
|
+
# an nmap source tree lying around, right?
|
122
|
+
cmd = run_cmd('cd nmap-5.51 ; ./configure')
|
123
|
+
|
124
|
+
# bc, to illustrate using stdin.
|
125
|
+
cmd = run_cmd('bc', "2^16\nquit")
|
126
|
+
|
127
|
+
# uncomment to see hash returned by run_cmd function.
|
128
|
+
# pp cmd
|
129
|
+
|
130
|
+
# test function with null command
|
131
|
+
cmd = run_cmd(nil)
|
metadata
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: open4
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease:
|
5
|
+
version: 1.1.0
|
5
6
|
platform: ruby
|
6
7
|
authors:
|
7
8
|
- Ara T. Howard
|
@@ -9,11 +10,10 @@ autorequire:
|
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
12
|
|
12
|
-
date:
|
13
|
-
default_executable:
|
13
|
+
date: 2011-06-24 00:00:00 Z
|
14
14
|
dependencies: []
|
15
15
|
|
16
|
-
description:
|
16
|
+
description: "description: open4 kicks the ass"
|
17
17
|
email: ara.t.howard@gmail.com
|
18
18
|
executables: []
|
19
19
|
|
@@ -22,21 +22,22 @@ extensions: []
|
|
22
22
|
extra_rdoc_files: []
|
23
23
|
|
24
24
|
files:
|
25
|
-
-
|
26
|
-
- open4.gemspec
|
27
|
-
- Rakefile
|
25
|
+
- LICENSE
|
28
26
|
- README
|
29
27
|
- README.erb
|
28
|
+
- Rakefile
|
29
|
+
- lib/open4.rb
|
30
|
+
- open4.gemspec
|
30
31
|
- samples/bg.rb
|
31
32
|
- samples/block.rb
|
32
33
|
- samples/exception.rb
|
34
|
+
- samples/jesse-caldwell.rb
|
33
35
|
- samples/simple.rb
|
34
36
|
- samples/spawn.rb
|
35
37
|
- samples/stdin_timeout.rb
|
36
38
|
- samples/timeout.rb
|
37
39
|
- white_box/leak.rb
|
38
|
-
|
39
|
-
homepage: http://github.com/ahoward/open4/tree/master
|
40
|
+
homepage: https://github.com/ahoward/open4
|
40
41
|
licenses: []
|
41
42
|
|
42
43
|
post_install_message:
|
@@ -45,21 +46,21 @@ rdoc_options: []
|
|
45
46
|
require_paths:
|
46
47
|
- lib
|
47
48
|
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
48
50
|
requirements:
|
49
51
|
- - ">="
|
50
52
|
- !ruby/object:Gem::Version
|
51
53
|
version: "0"
|
52
|
-
version:
|
53
54
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
54
56
|
requirements:
|
55
57
|
- - ">="
|
56
58
|
- !ruby/object:Gem::Version
|
57
59
|
version: "0"
|
58
|
-
version:
|
59
60
|
requirements: []
|
60
61
|
|
61
62
|
rubyforge_project: codeforpeople
|
62
|
-
rubygems_version: 1.
|
63
|
+
rubygems_version: 1.7.2
|
63
64
|
signing_key:
|
64
65
|
specification_version: 3
|
65
66
|
summary: open4
|