open4 0.9.6 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -8,162 +8,6 @@ SYNOPSIS
8
8
  open child process with handles on pid, stdin, stdout, and stderr: manage
9
9
  child processes and their io handles easily.
10
10
 
11
- HISTORY
12
-
13
- 0.9.5:
14
- - another patch from Corey Jewett, this time dealing with ruby's handling
15
- of chdir and threads. basically the 'cwd' keyword to open4 cannot work
16
- with multiple threads (aka background) because ruby cannot cause green
17
- threads to have an actuall different working dir. the moral is that the
18
- :cwd/'cwd' keyword to spawn will work with 0 or 1 threads in effect.
19
-
20
- 0.9.4:
21
- - patch to #background from Corey Jewett
22
-
23
-
24
- 0.9.3:
25
- - removed some debugging output accidentally left in 0.9.2. arggh!
26
-
27
- 0.9.2:
28
- - fixed a descriptor leak. thanks Andre Nathan.
29
-
30
- 0.9.1:
31
- - fixed warning with '-w' : @cid not initialized. thanks blaise tarr.
32
-
33
- 0.9.0:
34
- - added the ability for open4.spawn to take either an array of arguments
35
- or multiple arguments in order to specify the argv for the command run.
36
- for example
37
-
38
- open4.spawn ['touch', 'difficult to "quote"'], :stdout=>STDOUT
39
-
40
- same thing
41
-
42
- open4.spawn 'touch', 'difficult to "quote"', :stdout=>STDOUT
43
-
44
- thanks to jordan breeding for this suggestion
45
-
46
-
47
- - added 'cwd'/:cwd keyword. usage is pretty obivous
48
-
49
- open4.spawn 'pwd', 1=>STDOUT, :cwd=>'/tmp' #=> /tmp
50
-
51
- this one also from jordan
52
-
53
- 0.8.0:
54
-
55
- - fixed a critical bug whereby a process producing tons of stdout, but for
56
- which the stdout was not handled, would cause the child process to
57
- become blocked/hung writing to the pipe. eg, this command would cause a
58
- hang
59
-
60
- include Open4
61
-
62
- spawn 'ruby -e" puts Array.new(65536){ 42 } "'
63
-
64
- whereas this one would not
65
-
66
- include Open4
67
-
68
- spawn 'ruby -e" puts Array.new(65536){ 42 } "', :stdout=>StringIO.new
69
-
70
- this version handles the former by spawning a 'null' thread which reads,
71
- but does not process stdout/stderr. that way commands which generate
72
- tons of output will never become blocked.
73
-
74
- 0.7.0:
75
- - merged functionality of exitstatus/status keywords:
76
-
77
- include Open4
78
-
79
- spawn 'ruby -e "exit 42"' # raises
80
- spawn 'ruby -e "exit 42"', :status=>true # ok, returns status
81
- spawn 'ruby -e "exit 42"', :status=>42 # raises if status != 42
82
- spawn 'ruby -e "exit 42"', :status=>0,42 # raises if status != 0||42
83
-
84
- - the 0.6.0 was broken on rubyforge... this release fixes that (somehow!?)
85
-
86
- 0.6.0:
87
- - added feature for exitstatus to be list of acceptable exit statuses
88
-
89
- Open4.spawn 'ruby -e "exit 42"' # raises
90
- Open4.spawn 'ruby -e "exit 42"', :exitstatus=>[0,42] # ok
91
-
92
- - added :status switch, which will always simply return the status (no
93
- error thrown for failure)
94
-
95
- Open4.spawn 'ruby -e "exit 42"' # raises
96
- status = Open4.spawn 'ruby -e "exit 42"', :status=>true # ok
97
-
98
- note, however, that any SpawnError does in fact contain the failed
99
- status so, even when they are thrown, error status can be retrieved:
100
-
101
- include Open4
102
-
103
- status =
104
- begin
105
- spawn 'ruby -e "exit 42"'
106
- rescue SpawnError => e
107
- warn{ e }
108
- e.status
109
- end
110
-
111
- 0.5.1:
112
- - fixes a __critical__ but in ThreadEnsemble class that had a race
113
- condition that could cause thread deadlock. sorry bout that folks.
114
-
115
- 0.5.0:
116
- - on the suggestion of tim pease (thanks tim!), i added timeout features
117
- to open4. the command run may have an overall timeout and individual
118
- timeouts set for each of the io handles. for example
119
-
120
- cmd = 'command_that_produce_out_at_one_second_intervals'
121
-
122
- open4.spawn cmd, :stdout_timeout => 2
123
-
124
- or
125
-
126
- cmd = 'command_that_should_complete_in_about_one_minute'
127
-
128
- open4.spawn cmd, :timeout => 60
129
-
130
- or
131
-
132
- cmd = 'consumes_input_at_one_line_per_second_rate'
133
-
134
- input = %w( 42 forty-two 42.0 )
135
-
136
- open4.spawn cmd, :stdin=>input, :stdin_timeout=>1
137
-
138
- - added 'open4' alias so one can write
139
-
140
- open4.spawn vs Open4.spawn
141
-
142
- or even
143
-
144
- open4(cmd) do |pid,i,o,e|
145
- end
146
-
147
- - added signal info to SpawnError
148
-
149
- 0.4.0:
150
- - improved error handling contributed by jordan breeding.
151
- - introduction of background/bg method
152
-
153
- 0.3.0 :
154
- - bug fix from jordan breeding. general clean up. added spawn method.
155
-
156
- 0.2.0 :
157
- - added exception marshaled from child -> parent when exec fails. thanks
158
- to jordan breeding for a patch (yay!) and paul brannan for this most
159
- excellent idea.
160
-
161
- 0.1.0 :
162
- - fixed docs to correctly show return value of popen4 (pid first not last).
163
- thanks Stefanie Tellex <stefie10@alum.mit.edu> for catching this.
164
- 0.0.0 :
165
- - initial version
166
-
167
11
  INSTALL
168
12
 
169
13
  ~> gem install open4
@@ -349,9 +193,172 @@ SAMPLES
349
193
  42
350
194
  /dmsp/reference/ruby-1.8.1//lib/ruby/1.8/timeout.rb:42:in `relay': execution expired (Timeout::Error)
351
195
 
196
+ HISTORY
197
+ 1.0.0
198
+ - added ability for spawn to take a proc (respond_to?(:call))
199
+
200
+ cmd = ' ruby -e" 42.times{ puts 0b101010 } " '
201
+ include Open4
202
+ spawn cmd, :stdout => lambda{|buf| puts buf}
203
+
204
+
205
+ 0.9.5:
206
+ - another patch from Corey Jewett, this time dealing with ruby's handling
207
+ of chdir and threads. basically the 'cwd' keyword to open4 cannot work
208
+ with multiple threads (aka background) because ruby cannot cause green
209
+ threads to have an actuall different working dir. the moral is that the
210
+ :cwd/'cwd' keyword to spawn will work with 0 or 1 threads in effect.
211
+
212
+ 0.9.4:
213
+ - patch to #background from Corey Jewett
214
+
215
+
216
+ 0.9.3:
217
+ - removed some debugging output accidentally left in 0.9.2. arggh!
218
+
219
+ 0.9.2:
220
+ - fixed a descriptor leak. thanks Andre Nathan.
221
+
222
+ 0.9.1:
223
+ - fixed warning with '-w' : @cid not initialized. thanks blaise tarr.
224
+
225
+ 0.9.0:
226
+ - added the ability for open4.spawn to take either an array of arguments
227
+ or multiple arguments in order to specify the argv for the command run.
228
+ for example
229
+
230
+ open4.spawn ['touch', 'difficult to "quote"'], :stdout=>STDOUT
231
+
232
+ same thing
233
+
234
+ open4.spawn 'touch', 'difficult to "quote"', :stdout=>STDOUT
235
+
236
+ thanks to jordan breeding for this suggestion
237
+
238
+
239
+ - added 'cwd'/:cwd keyword. usage is pretty obivous
240
+
241
+ open4.spawn 'pwd', 1=>STDOUT, :cwd=>'/tmp' #=> /tmp
242
+
243
+ this one also from jordan
244
+
245
+ 0.8.0:
246
+
247
+ - fixed a critical bug whereby a process producing tons of stdout, but for
248
+ which the stdout was not handled, would cause the child process to
249
+ become blocked/hung writing to the pipe. eg, this command would cause a
250
+ hang
251
+
252
+ include Open4
253
+
254
+ spawn 'ruby -e" puts Array.new(65536){ 42 } "'
255
+
256
+ whereas this one would not
257
+
258
+ include Open4
259
+
260
+ spawn 'ruby -e" puts Array.new(65536){ 42 } "', :stdout=>StringIO.new
261
+
262
+ this version handles the former by spawning a 'null' thread which reads,
263
+ but does not process stdout/stderr. that way commands which generate
264
+ tons of output will never become blocked.
265
+
266
+ 0.7.0:
267
+ - merged functionality of exitstatus/status keywords:
268
+
269
+ include Open4
270
+
271
+ spawn 'ruby -e "exit 42"' # raises
272
+ spawn 'ruby -e "exit 42"', :status=>true # ok, returns status
273
+ spawn 'ruby -e "exit 42"', :status=>42 # raises if status != 42
274
+ spawn 'ruby -e "exit 42"', :status=>0,42 # raises if status != 0||42
275
+
276
+ - the 0.6.0 was broken on rubyforge... this release fixes that (somehow!?)
277
+
278
+ 0.6.0:
279
+ - added feature for exitstatus to be list of acceptable exit statuses
280
+
281
+ Open4.spawn 'ruby -e "exit 42"' # raises
282
+ Open4.spawn 'ruby -e "exit 42"', :exitstatus=>[0,42] # ok
283
+
284
+ - added :status switch, which will always simply return the status (no
285
+ error thrown for failure)
286
+
287
+ Open4.spawn 'ruby -e "exit 42"' # raises
288
+ status = Open4.spawn 'ruby -e "exit 42"', :status=>true # ok
289
+
290
+ note, however, that any SpawnError does in fact contain the failed
291
+ status so, even when they are thrown, error status can be retrieved:
292
+
293
+ include Open4
294
+
295
+ status =
296
+ begin
297
+ spawn 'ruby -e "exit 42"'
298
+ rescue SpawnError => e
299
+ warn{ e }
300
+ e.status
301
+ end
302
+
303
+ 0.5.1:
304
+ - fixes a __critical__ but in ThreadEnsemble class that had a race
305
+ condition that could cause thread deadlock. sorry bout that folks.
306
+
307
+ 0.5.0:
308
+ - on the suggestion of tim pease (thanks tim!), i added timeout features
309
+ to open4. the command run may have an overall timeout and individual
310
+ timeouts set for each of the io handles. for example
311
+
312
+ cmd = 'command_that_produce_out_at_one_second_intervals'
313
+
314
+ open4.spawn cmd, :stdout_timeout => 2
315
+
316
+ or
317
+
318
+ cmd = 'command_that_should_complete_in_about_one_minute'
319
+
320
+ open4.spawn cmd, :timeout => 60
321
+
322
+ or
323
+
324
+ cmd = 'consumes_input_at_one_line_per_second_rate'
325
+
326
+ input = %w( 42 forty-two 42.0 )
327
+
328
+ open4.spawn cmd, :stdin=>input, :stdin_timeout=>1
329
+
330
+ - added 'open4' alias so one can write
331
+
332
+ open4.spawn vs Open4.spawn
333
+
334
+ or even
335
+
336
+ open4(cmd) do |pid,i,o,e|
337
+ end
338
+
339
+ - added signal info to SpawnError
340
+
341
+ 0.4.0:
342
+ - improved error handling contributed by jordan breeding.
343
+ - introduction of background/bg method
344
+
345
+ 0.3.0 :
346
+ - bug fix from jordan breeding. general clean up. added spawn method.
347
+
348
+ 0.2.0 :
349
+ - added exception marshaled from child -> parent when exec fails. thanks
350
+ to jordan breeding for a patch (yay!) and paul brannan for this most
351
+ excellent idea.
352
+
353
+ 0.1.0 :
354
+ - fixed docs to correctly show return value of popen4 (pid first not last).
355
+ thanks Stefanie Tellex <stefie10@alum.mit.edu> for catching this.
356
+ 0.0.0 :
357
+ - initial version
358
+
352
359
  AUTHOR
353
360
 
354
- ara.t.howard@noaa.gov
361
+ ara.t.howard@gmail.com
355
362
 
356
363
  LICENSE
357
364
 
@@ -0,0 +1,365 @@
1
+ URIS
2
+
3
+ http://rubyforge.org/projects/codeforpeople/
4
+ http://www.codeforpeople.com/lib/ruby/
5
+
6
+ SYNOPSIS
7
+
8
+ open child process with handles on pid, stdin, stdout, and stderr: manage
9
+ child processes and their io handles easily.
10
+
11
+ INSTALL
12
+
13
+ ~> gem install open4
14
+
15
+ SAMPLES
16
+
17
+ ----------------------------------------------------------------------------
18
+ simple usage
19
+ ----------------------------------------------------------------------------
20
+
21
+ harp: > cat sample/simple.rb
22
+ require "open4"
23
+
24
+ pid, stdin, stdout, stderr = Open4::popen4 "sh"
25
+
26
+ stdin.puts "echo 42.out"
27
+ stdin.puts "echo 42.err 1>&2"
28
+ stdin.close
29
+
30
+ ignored, status = Process::waitpid2 pid
31
+
32
+ puts "pid : #{ pid }"
33
+ puts "stdout : #{ stdout.read.strip }"
34
+ puts "stderr : #{ stderr.read.strip }"
35
+ puts "status : #{ status.inspect }"
36
+ puts "exitstatus : #{ status.exitstatus }"
37
+
38
+
39
+ harp: > ruby sample/simple.rb
40
+ pid : 17273
41
+ stdout : 42.out
42
+ stderr : 42.err
43
+ status : #<Process::Status: pid=17273,exited(0)>
44
+ exitstatus : 0
45
+
46
+
47
+ ----------------------------------------------------------------------------
48
+ in block form - the child process is automatically waited for
49
+ ----------------------------------------------------------------------------
50
+
51
+ harp: > cat sample/block.rb
52
+ require 'open4'
53
+
54
+ status =
55
+ Open4::popen4("sh") do |pid, stdin, stdout, stderr|
56
+ stdin.puts "echo 42.out"
57
+ stdin.puts "echo 42.err 1>&2"
58
+ stdin.close
59
+
60
+ puts "pid : #{ pid }"
61
+ puts "stdout : #{ stdout.read.strip }"
62
+ puts "stderr : #{ stderr.read.strip }"
63
+ end
64
+
65
+ puts "status : #{ status.inspect }"
66
+ puts "exitstatus : #{ status.exitstatus }"
67
+
68
+
69
+ harp: > ruby sample/block.rb
70
+ pid : 17295
71
+ stdout : 42.out
72
+ stderr : 42.err
73
+ status : #<Process::Status: pid=17295,exited(0)>
74
+ exitstatus : 0
75
+
76
+ ----------------------------------------------------------------------------
77
+ exceptions are marshaled from child to parent if fork/exec fails
78
+ ----------------------------------------------------------------------------
79
+
80
+ harp: > cat sample/exception.rb
81
+ require "open4"
82
+ Open4::popen4 "noexist"
83
+
84
+
85
+ harp: > ruby sample/exception.rb
86
+ /dmsp/reference/ruby-1.8.1//lib/ruby/site_ruby/open4.rb:100:in `popen4': No such file or directory - noexist (Errno::ENOENT)
87
+ from sample/exception.rb:3
88
+
89
+ ----------------------------------------------------------------------------
90
+ the spawn method provides and even more convenient method of running a
91
+ process, allowing any object that supports 'each', 'read', or 'to_s' to be
92
+ given as stdin and any objects that support '<<' to be given as
93
+ stdout/stderr. an exception is thrown if the exec'd cmd fails (nonzero
94
+ exitstatus) unless the option 'raise'=>false is given
95
+ ----------------------------------------------------------------------------
96
+
97
+ harp: > cat sample/spawn.rb
98
+ require 'open4'
99
+ include Open4
100
+
101
+ cat = ' ruby -e" ARGF.each{|line| STDOUT << line} " '
102
+
103
+ stdout, stderr = '', ''
104
+ status = spawn cat, 'stdin' => '42', 'stdout' => stdout, 'stderr' => stderr
105
+ p status
106
+ p stdout
107
+ p stderr
108
+
109
+ stdout, stderr = '', ''
110
+ status = spawn cat, 0=>'42', 1=>stdout, 2=>stderr
111
+ p status
112
+ p stdout
113
+ p stderr
114
+
115
+
116
+ harp: > RUBYLIB=lib ruby sample/spawn.rb
117
+ 0
118
+ "42"
119
+ ""
120
+ 0
121
+ "42"
122
+ ""
123
+
124
+
125
+ ----------------------------------------------------------------------------
126
+ the bg/background method is similar to spawn, but the process is
127
+ automatically set running in a thread. the returned thread has several
128
+ methods added dynamically which return the pid and blocking calls to the
129
+ exitstatus.
130
+ ----------------------------------------------------------------------------
131
+
132
+ harp: > cat sample/bg.rb
133
+ require 'yaml'
134
+ require 'open4'
135
+ include Open4
136
+
137
+ stdin = '42'
138
+ stdout = ''
139
+ stderr = ''
140
+
141
+ t = bg 'ruby -e"sleep 4; puts ARGF.read"', 0=>stdin, 1=>stdout, 2=>stderr
142
+
143
+ waiter = Thread.new{ y t.pid => t.exitstatus } # t.exitstatus is a blocking call!
144
+
145
+ while((status = t.status))
146
+ y "status" => status
147
+ sleep 1
148
+ end
149
+
150
+ waiter.join
151
+
152
+ y "stdout" => stdout
153
+
154
+
155
+ harp: > ruby sample/bg.rb
156
+ ---
157
+ status: run
158
+ ---
159
+ status: sleep
160
+ ---
161
+ status: sleep
162
+ ---
163
+ status: sleep
164
+ ---
165
+ 21357: 0
166
+ ---
167
+ stdout: "42\n"
168
+
169
+ ----------------------------------------------------------------------------
170
+ the timeout methods can be used to ensure execution is preceding at the
171
+ desired interval. note also how to setup a 'pipeline'
172
+ ----------------------------------------------------------------------------
173
+
174
+ harp: > cat sample/stdin_timeout.rb
175
+ require 'open4'
176
+
177
+ producer = 'ruby -e" STDOUT.sync = true; loop{sleep(rand+rand) and puts 42} "'
178
+
179
+ consumer = 'ruby -e" STDOUT.sync = true; STDIN.each{|line| puts line} "'
180
+
181
+ open4(producer) do |pid, i, o, e|
182
+
183
+ open4.spawn consumer, :stdin=>o, :stdout=>STDOUT, :stdin_timeout => 1.4
184
+
185
+ end
186
+
187
+
188
+ harp: > ruby sample/stdin_timeout.rb
189
+ 42
190
+ 42
191
+ 42
192
+ 42
193
+ 42
194
+ /dmsp/reference/ruby-1.8.1//lib/ruby/1.8/timeout.rb:42:in `relay': execution expired (Timeout::Error)
195
+
196
+ HISTORY
197
+ 1.0.0
198
+ - added ability for spawn to take a proc (respond_to?(:call))
199
+
200
+ cmd = ' ruby -e" 42.times{ puts 0b101010 } " '
201
+ include Open4
202
+ spawn cmd, :stdout => lambda{|buf| puts buf}
203
+
204
+
205
+ 0.9.5:
206
+ - another patch from Corey Jewett, this time dealing with ruby's handling
207
+ of chdir and threads. basically the 'cwd' keyword to open4 cannot work
208
+ with multiple threads (aka background) because ruby cannot cause green
209
+ threads to have an actuall different working dir. the moral is that the
210
+ :cwd/'cwd' keyword to spawn will work with 0 or 1 threads in effect.
211
+
212
+ 0.9.4:
213
+ - patch to #background from Corey Jewett
214
+
215
+
216
+ 0.9.3:
217
+ - removed some debugging output accidentally left in 0.9.2. arggh!
218
+
219
+ 0.9.2:
220
+ - fixed a descriptor leak. thanks Andre Nathan.
221
+
222
+ 0.9.1:
223
+ - fixed warning with '-w' : @cid not initialized. thanks blaise tarr.
224
+
225
+ 0.9.0:
226
+ - added the ability for open4.spawn to take either an array of arguments
227
+ or multiple arguments in order to specify the argv for the command run.
228
+ for example
229
+
230
+ open4.spawn ['touch', 'difficult to "quote"'], :stdout=>STDOUT
231
+
232
+ same thing
233
+
234
+ open4.spawn 'touch', 'difficult to "quote"', :stdout=>STDOUT
235
+
236
+ thanks to jordan breeding for this suggestion
237
+
238
+
239
+ - added 'cwd'/:cwd keyword. usage is pretty obivous
240
+
241
+ open4.spawn 'pwd', 1=>STDOUT, :cwd=>'/tmp' #=> /tmp
242
+
243
+ this one also from jordan
244
+
245
+ 0.8.0:
246
+
247
+ - fixed a critical bug whereby a process producing tons of stdout, but for
248
+ which the stdout was not handled, would cause the child process to
249
+ become blocked/hung writing to the pipe. eg, this command would cause a
250
+ hang
251
+
252
+ include Open4
253
+
254
+ spawn 'ruby -e" puts Array.new(65536){ 42 } "'
255
+
256
+ whereas this one would not
257
+
258
+ include Open4
259
+
260
+ spawn 'ruby -e" puts Array.new(65536){ 42 } "', :stdout=>StringIO.new
261
+
262
+ this version handles the former by spawning a 'null' thread which reads,
263
+ but does not process stdout/stderr. that way commands which generate
264
+ tons of output will never become blocked.
265
+
266
+ 0.7.0:
267
+ - merged functionality of exitstatus/status keywords:
268
+
269
+ include Open4
270
+
271
+ spawn 'ruby -e "exit 42"' # raises
272
+ spawn 'ruby -e "exit 42"', :status=>true # ok, returns status
273
+ spawn 'ruby -e "exit 42"', :status=>42 # raises if status != 42
274
+ spawn 'ruby -e "exit 42"', :status=>0,42 # raises if status != 0||42
275
+
276
+ - the 0.6.0 was broken on rubyforge... this release fixes that (somehow!?)
277
+
278
+ 0.6.0:
279
+ - added feature for exitstatus to be list of acceptable exit statuses
280
+
281
+ Open4.spawn 'ruby -e "exit 42"' # raises
282
+ Open4.spawn 'ruby -e "exit 42"', :exitstatus=>[0,42] # ok
283
+
284
+ - added :status switch, which will always simply return the status (no
285
+ error thrown for failure)
286
+
287
+ Open4.spawn 'ruby -e "exit 42"' # raises
288
+ status = Open4.spawn 'ruby -e "exit 42"', :status=>true # ok
289
+
290
+ note, however, that any SpawnError does in fact contain the failed
291
+ status so, even when they are thrown, error status can be retrieved:
292
+
293
+ include Open4
294
+
295
+ status =
296
+ begin
297
+ spawn 'ruby -e "exit 42"'
298
+ rescue SpawnError => e
299
+ warn{ e }
300
+ e.status
301
+ end
302
+
303
+ 0.5.1:
304
+ - fixes a __critical__ but in ThreadEnsemble class that had a race
305
+ condition that could cause thread deadlock. sorry bout that folks.
306
+
307
+ 0.5.0:
308
+ - on the suggestion of tim pease (thanks tim!), i added timeout features
309
+ to open4. the command run may have an overall timeout and individual
310
+ timeouts set for each of the io handles. for example
311
+
312
+ cmd = 'command_that_produce_out_at_one_second_intervals'
313
+
314
+ open4.spawn cmd, :stdout_timeout => 2
315
+
316
+ or
317
+
318
+ cmd = 'command_that_should_complete_in_about_one_minute'
319
+
320
+ open4.spawn cmd, :timeout => 60
321
+
322
+ or
323
+
324
+ cmd = 'consumes_input_at_one_line_per_second_rate'
325
+
326
+ input = %w( 42 forty-two 42.0 )
327
+
328
+ open4.spawn cmd, :stdin=>input, :stdin_timeout=>1
329
+
330
+ - added 'open4' alias so one can write
331
+
332
+ open4.spawn vs Open4.spawn
333
+
334
+ or even
335
+
336
+ open4(cmd) do |pid,i,o,e|
337
+ end
338
+
339
+ - added signal info to SpawnError
340
+
341
+ 0.4.0:
342
+ - improved error handling contributed by jordan breeding.
343
+ - introduction of background/bg method
344
+
345
+ 0.3.0 :
346
+ - bug fix from jordan breeding. general clean up. added spawn method.
347
+
348
+ 0.2.0 :
349
+ - added exception marshaled from child -> parent when exec fails. thanks
350
+ to jordan breeding for a patch (yay!) and paul brannan for this most
351
+ excellent idea.
352
+
353
+ 0.1.0 :
354
+ - fixed docs to correctly show return value of popen4 (pid first not last).
355
+ thanks Stefanie Tellex <stefie10@alum.mit.edu> for catching this.
356
+ 0.0.0 :
357
+ - initial version
358
+
359
+ AUTHOR
360
+
361
+ ara.t.howard@gmail.com
362
+
363
+ LICENSE
364
+
365
+ ruby's
@@ -0,0 +1,225 @@
1
+
2
+ This.rubyforge_project = 'codeforpeople'
3
+ This.author = "Ara T. Howard"
4
+ This.email = "ara.t.howard@gmail.com"
5
+ This.homepage = "http://github.com/ahoward/#{ This.lib }/tree/master"
6
+
7
+
8
+ task :default do
9
+ puts(Rake::Task.tasks.map{|task| task.name} - ['default'])
10
+ end
11
+
12
+
13
+ task :gemspec do
14
+ ignore_extensions = 'git', 'svn', 'tmp', /sw./, 'bak', 'gem'
15
+ ignore_directories = 'pkg'
16
+
17
+ shiteless =
18
+ lambda do |list|
19
+ list.delete_if do |entry|
20
+ next unless test(?e, entry)
21
+ extension = File.basename(entry).split(%r/[.]/).last
22
+ ignore_extensions.any?{|ext| ext === extension}
23
+ end
24
+ list.delete_if do |entry|
25
+ next unless test(?d, entry)
26
+ dirname = File.expand_path(entry)
27
+ ignore_directories.any?{|dir| File.expand_path(dir) == dirname}
28
+ end
29
+ end
30
+
31
+ lib = This.lib
32
+ version = This.version
33
+ files = shiteless[Dir::glob("**/**")]
34
+ executables = shiteless[Dir::glob("bin/*")].map{|exe| File.basename(exe)}
35
+ has_rdoc = true #File.exist?('doc')
36
+ test_files = "test/#{ lib }.rb" if File.file?("test/#{ lib }.rb")
37
+
38
+ extensions = This.extensions
39
+ if extensions.nil?
40
+ %w( Makefile configure extconf.rb ).each do |ext|
41
+ extensions << ext if File.exists?(ext)
42
+ end
43
+ end
44
+ extensions = [extensions].flatten.compact
45
+
46
+ template =
47
+ if test(?e, 'gemspec.erb')
48
+ Template{ IO.read('gemspec.erb') }
49
+ else
50
+ Template {
51
+ <<-__
52
+ ## #{ lib }.gemspec
53
+ #
54
+
55
+ Gem::Specification::new do |spec|
56
+ spec.name = #{ lib.inspect }
57
+ spec.version = #{ version.inspect }
58
+ spec.platform = Gem::Platform::RUBY
59
+ spec.summary = #{ lib.inspect }
60
+
61
+ spec.description = "manage child processes and their io handles easily"
62
+
63
+ spec.files = #{ files.inspect }
64
+ spec.executables = #{ executables.inspect }
65
+
66
+ spec.require_path = "lib"
67
+
68
+ spec.has_rdoc = #{ has_rdoc.inspect }
69
+ spec.test_files = #{ test_files.inspect }
70
+ #spec.add_dependency 'lib', '>= version'
71
+ #spec.add_dependency 'fattr'
72
+
73
+ spec.extensions.push(*#{ extensions.inspect })
74
+
75
+ spec.rubyforge_project = #{ This.rubyforge_project.inspect }
76
+ spec.author = #{ This.author.inspect }
77
+ spec.email = #{ This.email.inspect }
78
+ spec.homepage = #{ This.homepage.inspect }
79
+ end
80
+ __
81
+ }
82
+ end
83
+
84
+ open("#{ lib }.gemspec", "w"){|fd| fd.puts template}
85
+ This.gemspec = "#{ lib }.gemspec"
86
+ end
87
+
88
+ task :gem => [:clean, :gemspec] do
89
+ Fu.mkdir_p This.pkgdir
90
+ before = Dir['*.gem']
91
+ cmd = "gem build #{ This.gemspec }"
92
+ `#{ cmd }`
93
+ after = Dir['*.gem']
94
+ gem = ((after - before).first || after.first) or abort('no gem!')
95
+ Fu.mv gem, This.pkgdir
96
+ This.gem = File.basename(gem)
97
+ end
98
+
99
+
100
+ task :readme do
101
+ samples = ''
102
+ prompt = '~ > '
103
+ lib = This.lib
104
+ version = This.version
105
+
106
+ Dir['sample*/*'].sort.each do |sample|
107
+ samples << "\n" << " <========< #{ sample } >========>" << "\n\n"
108
+
109
+ cmd = "cat #{ sample }"
110
+ samples << Util.indent(prompt + cmd, 2) << "\n\n"
111
+ samples << Util.indent(`#{ cmd }`, 4) << "\n"
112
+
113
+ cmd = "ruby #{ sample }"
114
+ samples << Util.indent(prompt + cmd, 2) << "\n\n"
115
+
116
+ cmd = "ruby -e'STDOUT.sync=true; exec %(ruby -Ilib #{ sample })'"
117
+ samples << Util.indent(`#{ cmd } 2>&1`, 4) << "\n"
118
+ end
119
+
120
+ template =
121
+ if test(?e, 'readme.erb')
122
+ Template{ IO.read('readme.erb') }
123
+ else
124
+ Template {
125
+ <<-__
126
+ NAME
127
+ #{ lib }
128
+
129
+ DESCRIPTION
130
+
131
+ INSTALL
132
+ gem install #{ lib }
133
+
134
+ SAMPLES
135
+ #{ samples }
136
+ __
137
+ }
138
+ end
139
+
140
+ open("README", "w"){|fd| fd.puts template}
141
+ end
142
+
143
+
144
+ task :clean do
145
+ Dir[File.join(This.pkgdir, '**/**')].each{|entry| Fu.rm_rf(entry)}
146
+ end
147
+
148
+
149
+ task :release => [:clean, :gemspec, :gem] do
150
+ gems = Dir[File.join(This.pkgdir, '*.gem')].flatten
151
+ raise "which one? : #{ gems.inspect }" if gems.size > 1
152
+ raise "no gems?" if gems.size < 1
153
+ cmd = "rubyforge login && rubyforge add_release #{ This.rubyforge_project } #{ This.lib } #{ This.version } #{ This.pkgdir }/#{ This.gem }"
154
+ puts cmd
155
+ system cmd
156
+ end
157
+
158
+
159
+
160
+
161
+
162
+ BEGIN {
163
+ $VERBOSE = nil
164
+
165
+ require 'ostruct'
166
+ require 'erb'
167
+ require 'fileutils'
168
+
169
+ Fu = FileUtils
170
+
171
+ This = OpenStruct.new
172
+
173
+ This.file = File.expand_path(__FILE__)
174
+ This.dir = File.dirname(This.file)
175
+ This.pkgdir = File.join(This.dir, 'pkg')
176
+
177
+ lib = ENV['LIB']
178
+ unless lib
179
+ lib = File.basename(Dir.pwd)
180
+ end
181
+ This.lib = lib
182
+
183
+ version = ENV['VERSION']
184
+ unless version
185
+ name = lib.capitalize
186
+ require "./lib/#{ lib }"
187
+ version = eval(name).send(:version)
188
+ end
189
+ This.version = version
190
+
191
+ abort('no lib') unless This.lib
192
+ abort('no version') unless This.version
193
+
194
+ module Util
195
+ def indent(s, n = 2)
196
+ s = unindent(s)
197
+ ws = ' ' * n
198
+ s.gsub(%r/^/, ws)
199
+ end
200
+
201
+ def unindent(s)
202
+ indent = nil
203
+ s.each do |line|
204
+ next if line =~ %r/^\s*$/
205
+ indent = line[%r/^\s*/] and break
206
+ end
207
+ indent ? s.gsub(%r/^#{ indent }/, "") : s
208
+ end
209
+ extend self
210
+ end
211
+
212
+ class Template
213
+ def initialize(&block)
214
+ @block = block
215
+ @template = block.call.to_s
216
+ end
217
+ def expand(b=nil)
218
+ ERB.new(Util.unindent(@template)).result(b||@block)
219
+ end
220
+ alias_method 'to_s', 'expand'
221
+ end
222
+ def Template(*args, &block) Template.new(*args, &block) end
223
+
224
+ Dir.chdir(This.dir)
225
+ }
@@ -5,7 +5,7 @@ require 'thread'
5
5
 
6
6
  module Open4
7
7
  #--{{{
8
- VERSION = '0.9.6'
8
+ VERSION = '1.0.1'
9
9
  def self.version() VERSION end
10
10
 
11
11
  class Error < ::StandardError; end
@@ -17,6 +17,7 @@ module Open4
17
17
  verbose = $VERBOSE
18
18
  begin
19
19
  $VERBOSE = nil
20
+ ps.first.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
20
21
  ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
21
22
 
22
23
  cid = fork {
@@ -196,10 +197,17 @@ module Open4
196
197
 
197
198
  def relay src, dst = nil, t = nil
198
199
  #--{{{
200
+ send_dst =
201
+ if dst.respond_to?(:call)
202
+ lambda{|buf| dst.call(buf)}
203
+ else
204
+ lambda{|buf| dst << buf}
205
+ end
206
+
199
207
  unless src.nil?
200
208
  if src.respond_to? :gets
201
209
  while buf = to(t){ src.gets }
202
- dst << buf if dst
210
+ send_dst[buf]
203
211
  end
204
212
 
205
213
  elsif src.respond_to? :each
@@ -218,7 +226,7 @@ module Open4
218
226
  begin
219
227
  src.each do |buf|
220
228
  timer_cancel[t]
221
- dst << buf if dst
229
+ send_dst[buf]
222
230
  timer_set[t]
223
231
  end
224
232
  ensure
@@ -227,11 +235,11 @@ module Open4
227
235
 
228
236
  elsif src.respond_to? :read
229
237
  buf = to(t){ src.read }
230
- dst << buf if dst
238
+ send_dst[buf]
231
239
 
232
240
  else
233
241
  buf = to(t){ src.to_s }
234
- dst << buf if dst
242
+ send_dst[buf]
235
243
  end
236
244
  end
237
245
  #--}}}
@@ -0,0 +1,28 @@
1
+ ## open4.gemspec
2
+ #
3
+
4
+ Gem::Specification::new do |spec|
5
+ spec.name = "open4"
6
+ spec.version = "1.0.1"
7
+ spec.platform = Gem::Platform::RUBY
8
+ spec.summary = "open4"
9
+
10
+ spec.description = "manage child processes and their io handles easily"
11
+
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
+ spec.executables = []
14
+
15
+ spec.require_path = "lib"
16
+
17
+ spec.has_rdoc = true
18
+ spec.test_files = nil
19
+ #spec.add_dependency 'lib', '>= version'
20
+ #spec.add_dependency 'fattr'
21
+
22
+ spec.extensions.push(*[])
23
+
24
+ spec.rubyforge_project = "codeforpeople"
25
+ spec.author = "Ara T. Howard"
26
+ spec.email = "ara.t.howard@gmail.com"
27
+ spec.homepage = "http://github.com/ahoward/open4/tree/master"
28
+ end
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
metadata CHANGED
@@ -1,60 +1,67 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.2
3
- specification_version: 1
4
2
  name: open4
5
3
  version: !ruby/object:Gem::Version
6
- version: 0.9.6
7
- date: 2007-07-18 00:00:00 -06:00
8
- summary: open4
9
- require_paths:
10
- - lib
11
- email: ara.t.howard@noaa.gov
12
- homepage: http://codeforpeople.com/lib/ruby/open4/
13
- rubyforge_project:
14
- description:
15
- autorequire: open4
16
- default_executable:
17
- bindir: bin
18
- has_rdoc: false
19
- required_ruby_version: !ruby/object:Gem::Version::Requirement
20
- requirements:
21
- - - ">"
22
- - !ruby/object:Gem::Version
23
- version: 0.0.0
24
- version:
4
+ version: 1.0.1
25
5
  platform: ruby
26
- signing_key:
27
- cert_chain:
28
- post_install_message:
29
6
  authors:
30
7
  - Ara T. Howard
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-18 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: manage child processes and their io handles easily
17
+ email: ara.t.howard@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
31
24
  files:
32
- - gemspec.rb
33
- - install.rb
34
- - lib
35
25
  - lib/open4.rb
26
+ - open4.gemspec
27
+ - Rakefile
36
28
  - README
37
- - sample
38
- - sample/bg.rb
39
- - sample/block.rb
40
- - sample/exception.rb
41
- - sample/simple.rb
42
- - sample/spawn.rb
43
- - sample/stdin_timeout.rb
44
- - sample/timeout.rb
45
- - white_box
29
+ - README.erb
30
+ - samples/bg.rb
31
+ - samples/block.rb
32
+ - samples/exception.rb
33
+ - samples/simple.rb
34
+ - samples/spawn.rb
35
+ - samples/stdin_timeout.rb
36
+ - samples/timeout.rb
46
37
  - white_box/leak.rb
47
- test_files: []
38
+ has_rdoc: true
39
+ homepage: http://github.com/ahoward/open4/tree/master
40
+ licenses: []
48
41
 
42
+ post_install_message:
49
43
  rdoc_options: []
50
44
 
51
- extra_rdoc_files: []
52
-
53
- executables: []
54
-
55
- extensions: []
56
-
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
57
59
  requirements: []
58
60
 
59
- dependencies: []
61
+ rubyforge_project: codeforpeople
62
+ rubygems_version: 1.3.5
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: open4
66
+ test_files: []
60
67
 
data/gemspec.rb DELETED
@@ -1,23 +0,0 @@
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
data/install.rb DELETED
@@ -1,201 +0,0 @@
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
- -h, --help
160
- usage
161
-
162
- begin
163
- while switch = ARGV.switch
164
- case switch
165
- when '-d', '--destdir'
166
- libdir = ARGV.req_arg
167
- when '-l', '--libdir'
168
- libdir = ARGV.req_arg
169
- when '-b', '--bindir'
170
- bindir = ARGV.req_arg
171
- when '-r', '--ruby'
172
- $ruby = ARGV.req_arg
173
- when '-n', '--no_linkify'
174
- no_linkify = true
175
- when '-h', '--help'
176
- help = true
177
- else
178
- raise "unknown switch #{switch.dump}"
179
- end
180
- end
181
- rescue
182
- STDERR.puts $!.to_s
183
- STDERR.puts usage
184
- exit 1
185
- end
186
-
187
- if help
188
- STDOUT.puts usage
189
- exit
190
- end
191
-
192
- unless no_linkify
193
- linked = linkify('lib') + linkify('bin')
194
- end
195
-
196
- install_rb(LIBDIR, libdir, LIBDIR_MODE)
197
- install_rb(BINDIR, bindir, BINDIR_MODE, bin=true)
198
-
199
- if linked
200
- linked.each{|path| File::rm_f path}
201
- end