open4 0.2.0 → 0.3.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/README CHANGED
@@ -3,14 +3,15 @@ URIS
3
3
  http://rubyforge.org/frs/?group_id=1024
4
4
  http://www.codeforpeople.com/lib/ruby/
5
5
 
6
-
7
6
  SYNOPSIS
8
7
 
9
8
  open child process with handles on pid, stdin, stdout, and stderr
10
9
 
11
-
12
10
  HISTORY
13
11
 
12
+ 0.3.0 :
13
+ - bug fix from jordan breeding. general clean up. added spawn method.
14
+
14
15
  0.2.0 :
15
16
  - added exception marshaled from child -> parent when exec fails. thanks
16
17
  to jordan breeding for a patch (yay!) and paul brannan for this most
@@ -22,17 +23,17 @@ HISTORY
22
23
  0.0.0 :
23
24
  - initial version
24
25
 
25
-
26
26
  INSTALL
27
27
 
28
28
  ~> gem install open4
29
29
 
30
-
31
30
  SAMPLES
32
31
 
33
- simple usage:
32
+ ----------------------------------------------------------------------------
33
+ simple usage
34
+ ----------------------------------------------------------------------------
34
35
 
35
- jib:~/eg/ruby/open4/open4-0.2.0 > cat sample/simple.rb
36
+ harp: > cat sample/simple.rb
36
37
  require "open4"
37
38
 
38
39
  pid, stdin, stdout, stderr = Open4::popen4 "sh"
@@ -50,7 +51,7 @@ SAMPLES
50
51
  puts "exitstatus : #{ status.exitstatus }"
51
52
 
52
53
 
53
- jib:~/eg/ruby/open4/open4-0.2.0 > ruby sample/simple.rb
54
+ harp: > ruby sample/simple.rb
54
55
  pid : 17273
55
56
  stdout : 42.out
56
57
  stderr : 42.err
@@ -58,10 +59,11 @@ SAMPLES
58
59
  exitstatus : 0
59
60
 
60
61
 
61
- block form - child process is automatically waited for:
62
-
62
+ ----------------------------------------------------------------------------
63
+ in block form - the child process is automatically waited for
64
+ ----------------------------------------------------------------------------
63
65
 
64
- jib:~/eg/ruby/open4/open4-0.2.0 > cat sample/block.rb
66
+ harp: > cat sample/block.rb
65
67
  require 'open4'
66
68
 
67
69
  status =
@@ -79,32 +81,65 @@ SAMPLES
79
81
  puts "exitstatus : #{ status.exitstatus }"
80
82
 
81
83
 
82
- jib:~/eg/ruby/open4/open4-0.2.0 > ruby sample/block.rb
84
+ harp: > ruby sample/block.rb
83
85
  pid : 17295
84
86
  stdout : 42.out
85
87
  stderr : 42.err
86
88
  status : #<Process::Status: pid=17295,exited(0)>
87
89
  exitstatus : 0
88
90
 
91
+ ----------------------------------------------------------------------------
92
+ exceptions are marshaled from child to parent if fork/exec fails
93
+ ----------------------------------------------------------------------------
89
94
 
90
- exceptions are marshaled from child to parent if fork/exec fails:
91
-
92
-
93
- jib:~/eg/ruby/open4/open4-0.2.0 > cat sample/exception.rb
95
+ harp: > cat sample/exception.rb
94
96
  require "open4"
95
97
  Open4::popen4 "noexist"
96
98
 
97
99
 
98
- jib:~/eg/ruby/open4/open4-0.2.0 > ruby sample/exception.rb
100
+ harp: > ruby sample/exception.rb
99
101
  /dmsp/reference/ruby-1.8.1//lib/ruby/site_ruby/open4.rb:100:in `popen4': No such file or directory - noexist (Errno::ENOENT)
100
102
  from sample/exception.rb:3
101
103
 
104
+ ----------------------------------------------------------------------------
105
+ the spawn method provides and even more convenient method of running a
106
+ process, allowing any object that supports 'each', 'read', or 'to_s' to be
107
+ given as stdin and any objects that support '<<' to be given as
108
+ stdout/stderr. an exception is thrown if the exec'd cmd fails (nonzero
109
+ exitstatus) unless the option 'raise'=>false is given
110
+ ----------------------------------------------------------------------------
111
+
112
+ harp: > cat sample/spawn.rb
113
+ require 'open4'
114
+ include Open4
115
+
116
+ cat = ' ruby -e" ARGF.each{|line| STDOUT << line} " '
117
+
118
+ stdout, stderr = '', ''
119
+ status = spawn cat, 'stdin' => '42', 'stdout' => stdout, 'stderr' => stderr
120
+ p status
121
+ p stdout
122
+ p stderr
123
+
124
+ stdout, stderr = '', ''
125
+ status = spawn cat, 0=>'42', 1=>stdout, 2=>stderr
126
+ p status
127
+ p stdout
128
+ p stderr
129
+
130
+
131
+ harp: > RUBYLIB=lib ruby sample/spawn.rb
132
+ 0
133
+ "42"
134
+ ""
135
+ 0
136
+ "42"
137
+ ""
102
138
 
103
139
  AUTHOR
104
140
 
105
141
  ara.t.howard@noaa.gov
106
142
 
107
-
108
143
  LICENSE
109
144
 
110
145
  ruby's
File without changes
@@ -0,0 +1,133 @@
1
+ require 'fcntl'
2
+
3
+ module Open4
4
+ #--{{{
5
+ def self.version() '0.3.0' end
6
+
7
+ def popen4(*cmd)
8
+ #--{{{
9
+ pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
10
+
11
+ verbose = $VERBOSE
12
+ begin
13
+ $VERBOSE = nil
14
+ ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
15
+
16
+ cid = fork {
17
+ pw.last.close
18
+ STDIN.reopen pw.first
19
+ pw.first.close
20
+
21
+ pr.first.close
22
+ STDOUT.reopen pr.last
23
+ pr.last.close
24
+
25
+ pe.first.close
26
+ STDERR.reopen pe.last
27
+ pe.last.close
28
+
29
+ STDOUT.sync = STDERR.sync = true
30
+
31
+ begin
32
+ exec(*cmd)
33
+ raise "exec failed!"
34
+ rescue Exception => e
35
+ Marshal.dump(e, ps.last)
36
+ ps.last.flush
37
+ end
38
+ ps.last.close unless (ps.last.closed?)
39
+ exit!
40
+ }
41
+ ensure
42
+ $VERBOSE = verbose
43
+ end
44
+
45
+ [pw.first, pr.last, pe.last, ps.last].each{|fd| fd.close}
46
+
47
+ begin
48
+ e = Marshal.load ps.first
49
+ if Exception === e
50
+ raise e
51
+ else
52
+ raise "unknown failure"
53
+ end
54
+ rescue EOFError # If we get an EOF error, then the exec was successful
55
+ 42
56
+ end
57
+
58
+ pw.last.sync = true
59
+
60
+ pi = [pw.last, pr.first, pe.first]
61
+
62
+ if defined? yield
63
+ begin
64
+ yield(cid, *pi)
65
+ Process.waitpid2(cid).last
66
+ ensure
67
+ pi.each{|fd| fd.close unless fd.closed?}
68
+ end
69
+ else
70
+ [cid, pw.last, pr.first, pe.first]
71
+ end
72
+ #--}}}
73
+ end
74
+ alias open4 popen4
75
+ module_function :popen4
76
+ module_function :open4
77
+
78
+ def spawn cmd, opts = {}
79
+ #--{{{
80
+ getopt = lambda do |*args|
81
+ keys, default, ignored = args
82
+ catch('opt') do
83
+ [keys].flatten.each do |key|
84
+ [key, key.to_s, key.to_s.intern].each do |key|
85
+ throw 'opt', opts[key] if opts.has_key?(key)
86
+ end
87
+ end
88
+ default
89
+ end
90
+ end
91
+
92
+ dont_raise = getopt[ 'quiet', !getopt['raise', true] ]
93
+ exitstatus = getopt[ %w( exitstatus exit_status status ), 0 ]
94
+ stdin = getopt[ ['stdin', 'in', '0', 0] ]
95
+ stdout = getopt[ ['stdout', 'out', '1', 1] ]
96
+ stderr = getopt[ ['stderr', 'err', '2', 2] ]
97
+
98
+ started = false
99
+
100
+ status =
101
+ begin
102
+ popen4(cmd) do |c, i, o, e|
103
+ started = true
104
+
105
+ if stdin.respond_to? :each
106
+ stdin.each{|buf| i << buf}
107
+ elsif stdin.respond_to? :read
108
+ i << stdin.read
109
+ else
110
+ i << stdin.to_s
111
+ end
112
+
113
+ i.close
114
+
115
+ ot = Thread.new(o){ o.each{|buf| stdout << buf if stdout} }
116
+ et = Thread.new(e){ e.each{|buf| stderr << buf if stderr} }
117
+
118
+ ot.join
119
+ et.join
120
+ end
121
+ rescue
122
+ raise unless(not started and dont_raise)
123
+ end
124
+
125
+ raise "cmd <#{ cmd }> failed with <#{ status }>" unless
126
+ ((status == exitstatus) or dont_raise)
127
+
128
+ status
129
+ #--}}}
130
+ end
131
+ module_function :spawn
132
+ #--}}}
133
+ end
@@ -1,131 +1,133 @@
1
- # vim: ts=2 sts=2 et
2
- # stolen directly from Open3::open3.rb!
3
- #
4
- # open4.rb: Spawn a program like popen, but with stderr, and pid, too. You might
5
- # also want to use this if you want to bypass the shell. (By passing multiple
6
- # args, which IO#popen does not allow)
7
- #
8
- # Usage:
9
- # require "open4"
10
- #
11
- # pid, stdin, stdout, stderr = Open4.popen4('nroff -man')
12
- # or
13
- # include Open4
14
- # pid, stdin, stdout, stderr = popen4('nroff -man')
15
-
16
1
  require 'fcntl'
17
2
 
18
3
  module Open4
19
4
  #--{{{
20
-
21
- if false
22
- PROCESSES = Hash.new
23
-
24
- orig_sigchld_handler = trap("CLD") do |*args|
25
- begin
26
- PROCESSES.each do |pid, foo|
27
- if (Process.waitpid2(pid, Process::WNOHANG) == pid)
28
- PROCESSES.delete(pid)
29
- end
30
- end
31
- rescue Errno::ECHILD
32
- nil
33
- end
34
- orig_sigchld_handler.call(*args) if (not (orig_sigchld_handler.nil?))
35
- end
36
- end
37
-
38
- #
39
- # [ pid, stdin, stdout, stderr ] = popen4 command
40
- #
5
+ def self.version() '0.3.0' end
41
6
 
42
7
  def popen4(*cmd)
43
8
  #--{{{
44
-
45
- pw = IO::pipe # pipe[1] for read, pipe[0] for write
46
- pr = IO::pipe # pipe[0] for read, pipe[1] for write
47
- pe = IO::pipe # pipe[0] for read, pipe[1] for write
48
- ps = IO::pipe # pipe[0] for read, pipe[1] for write
9
+ pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
49
10
 
50
11
  verbose = $VERBOSE
51
12
  begin
52
- $VERBOSE = nil # shut up warning about forking in threads, world writable
53
- # dirs, etc
54
-
55
- ps[1].fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
13
+ $VERBOSE = nil
14
+ ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
56
15
 
57
16
  cid = fork {
58
- # child
59
- pw[1].close
60
- STDIN.reopen(pw[0])
61
- pw[0].close
17
+ pw.last.close
18
+ STDIN.reopen pw.first
19
+ pw.first.close
62
20
 
63
- pr[0].close
64
- STDOUT.reopen(pr[1])
65
- pr[1].close
21
+ pr.first.close
22
+ STDOUT.reopen pr.last
23
+ pr.last.close
66
24
 
67
- pe[0].close
68
- STDERR.reopen(pe[1])
69
- pe[1].close
25
+ pe.first.close
26
+ STDERR.reopen pe.last
27
+ pe.last.close
70
28
 
71
- STDOUT.sync = true
72
- STDERR.sync = true
29
+ STDOUT.sync = STDERR.sync = true
73
30
 
74
31
  begin
75
32
  exec(*cmd)
76
- raise "exec returned!"
77
- rescue Exception
78
- Marshal.dump($!, ps[1])
79
- ps[1].flush
33
+ raise "exec failed!"
34
+ rescue Exception => e
35
+ Marshal.dump(e, ps.last)
36
+ ps.last.flush
80
37
  end
81
- ps[1].close unless (ps[1].closed?)
38
+ ps.last.close unless (ps.last.closed?)
82
39
  exit!
83
40
  }
84
41
  ensure
85
42
  $VERBOSE = verbose
86
43
  end
87
44
 
88
- pw[0].close
89
- pr[1].close
90
- pe[1].close
91
- ps[1].close
45
+ [pw.first, pr.last, pe.last, ps.last].each{|fd| fd.close}
92
46
 
93
- exc = nil
94
47
  begin
95
- exc = Marshal.load(ps[0])
48
+ e = Marshal.load ps.first
49
+ if Exception === e
50
+ raise e
51
+ else
52
+ raise "unknown failure"
53
+ end
96
54
  rescue EOFError # If we get an EOF error, then the exec was successful
55
+ 42
97
56
  end
98
57
 
99
- if (not (exc.nil?)) then
100
- raise exc
101
- end
58
+ pw.last.sync = true
102
59
 
103
- # PROCESSES[cid] = true
60
+ pi = [pw.last, pr.first, pe.first]
104
61
 
105
- pi = [pw[1], pr[0], pe[0]]
106
- pw[1].sync = true
107
62
  if defined? yield
108
63
  begin
109
64
  yield(cid, *pi)
110
- return((Process.waitpid2(cid, 1)).last)
65
+ Process.waitpid2(cid).last
111
66
  ensure
112
- pi.each{|p| p.close unless p.closed?}
67
+ pi.each{|fd| fd.close unless fd.closed?}
113
68
  end
69
+ else
70
+ [cid, pw.last, pr.first, pe.first]
114
71
  end
115
- [cid, pw[1], pr[0], pe[0]]
116
72
  #--}}}
117
73
  end
118
-
119
74
  alias open4 popen4
120
75
  module_function :popen4
121
76
  module_function :open4
122
77
 
123
- #--}}}
124
- end
78
+ def spawn cmd, opts = {}
79
+ #--{{{
80
+ getopt = lambda do |*args|
81
+ keys, default, ignored = args
82
+ catch('opt') do
83
+ [keys].flatten.each do |key|
84
+ [key, key.to_s, key.to_s.intern].each do |key|
85
+ throw 'opt', opts[key] if opts.has_key?(key)
86
+ end
87
+ end
88
+ default
89
+ end
90
+ end
91
+
92
+ dont_raise = getopt[ 'quiet', !getopt['raise', true] ]
93
+ exitstatus = getopt[ %w( exitstatus exit_status status ), 0 ]
94
+ stdin = getopt[ ['stdin', 'in', '0', 0] ]
95
+ stdout = getopt[ ['stdout', 'out', '1', 1] ]
96
+ stderr = getopt[ ['stderr', 'err', '2', 2] ]
97
+
98
+ started = false
99
+
100
+ status =
101
+ begin
102
+ popen4(cmd) do |c, i, o, e|
103
+ started = true
104
+
105
+ if stdin.respond_to? :each
106
+ stdin.each{|buf| i << buf}
107
+ elsif stdin.respond_to? :read
108
+ i << stdin.read
109
+ else
110
+ i << stdin.to_s
111
+ end
112
+
113
+ i.close
125
114
 
126
- if $0 == __FILE__
127
- status = Open4::popen4('sh'){|cid,i,o,e|i.puts 'echo 42';i.close; puts o.read;}
128
- p [status]
129
- p status.exitstatus
130
- p status == 0
115
+ ot = Thread.new(o){ o.each{|buf| stdout << buf if stdout} }
116
+ et = Thread.new(e){ e.each{|buf| stderr << buf if stderr} }
117
+
118
+ ot.join
119
+ et.join
120
+ end
121
+ rescue
122
+ raise unless(not started and dont_raise)
123
+ end
124
+
125
+ raise "cmd <#{ cmd }> failed with <#{ status }>" unless
126
+ ((status == exitstatus) or dont_raise)
127
+
128
+ status
129
+ #--}}}
130
+ end
131
+ module_function :spawn
132
+ #--}}}
131
133
  end
File without changes
@@ -0,0 +1,16 @@
1
+ require 'open4'
2
+ include Open4
3
+
4
+ cat = 'ruby -e" ARGF.each{|line| STDOUT << line} "'
5
+
6
+ stdout, stderr = '', ''
7
+ status = spawn cat, 'stdin' => '42', 'stdout' => stdout, 'stderr' => stderr
8
+ p status
9
+ p stdout
10
+ p stderr
11
+
12
+ stdout, stderr = '', ''
13
+ status = spawn cat, 0=>'42', 1=>stdout, 2=>stderr
14
+ p status
15
+ p stdout
16
+ p stderr
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: open4
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.0
7
- date: 2006-02-13 00:00:00.000000 -07:00
6
+ version: 0.3.0
7
+ date: 2006-04-19 00:00:00.000000 -06:00
8
8
  summary: open4
9
9
  require_paths:
10
10
  - lib
@@ -29,18 +29,20 @@ cert_chain:
29
29
  authors:
30
30
  - Ara T. Howard
31
31
  files:
32
- - lib
33
- - install
32
+ - open4-0.3.0.gem
34
33
  - install.rb
35
- - README
36
34
  - sample
35
+ - lib
36
+ - README
37
+ - build
38
+ - install
37
39
  - gemspec.rb
38
- - open4-0.2.0.gem
39
- - lib/open4.rb
40
- - lib/open4-0.2.0.rb
41
40
  - sample/block.rb
42
41
  - sample/simple.rb
43
42
  - sample/exception.rb
43
+ - sample/spawn.rb
44
+ - lib/open4-0.3.0.rb
45
+ - lib/open4.rb
44
46
  test_files: []
45
47
  rdoc_options: []
46
48
  extra_rdoc_files: []
@@ -1,131 +0,0 @@
1
- # vim: ts=2 sts=2 et
2
- # stolen directly from Open3::open3.rb!
3
- #
4
- # open4.rb: Spawn a program like popen, but with stderr, and pid, too. You might
5
- # also want to use this if you want to bypass the shell. (By passing multiple
6
- # args, which IO#popen does not allow)
7
- #
8
- # Usage:
9
- # require "open4"
10
- #
11
- # pid, stdin, stdout, stderr = Open4.popen4('nroff -man')
12
- # or
13
- # include Open4
14
- # pid, stdin, stdout, stderr = popen4('nroff -man')
15
-
16
- require 'fcntl'
17
-
18
- module Open4
19
- #--{{{
20
-
21
- if false
22
- PROCESSES = Hash.new
23
-
24
- orig_sigchld_handler = trap("CLD") do |*args|
25
- begin
26
- PROCESSES.each do |pid, foo|
27
- if (Process.waitpid2(pid, Process::WNOHANG) == pid)
28
- PROCESSES.delete(pid)
29
- end
30
- end
31
- rescue Errno::ECHILD
32
- nil
33
- end
34
- orig_sigchld_handler.call(*args) if (not (orig_sigchld_handler.nil?))
35
- end
36
- end
37
-
38
- #
39
- # [ pid, stdin, stdout, stderr ] = popen4 command
40
- #
41
-
42
- def popen4(*cmd)
43
- #--{{{
44
-
45
- pw = IO::pipe # pipe[1] for read, pipe[0] for write
46
- pr = IO::pipe # pipe[0] for read, pipe[1] for write
47
- pe = IO::pipe # pipe[0] for read, pipe[1] for write
48
- ps = IO::pipe # pipe[0] for read, pipe[1] for write
49
-
50
- verbose = $VERBOSE
51
- begin
52
- $VERBOSE = nil # shut up warning about forking in threads, world writable
53
- # dirs, etc
54
-
55
- ps[1].fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
56
-
57
- cid = fork {
58
- # child
59
- pw[1].close
60
- STDIN.reopen(pw[0])
61
- pw[0].close
62
-
63
- pr[0].close
64
- STDOUT.reopen(pr[1])
65
- pr[1].close
66
-
67
- pe[0].close
68
- STDERR.reopen(pe[1])
69
- pe[1].close
70
-
71
- STDOUT.sync = true
72
- STDERR.sync = true
73
-
74
- begin
75
- exec(*cmd)
76
- raise "exec returned!"
77
- rescue Exception
78
- Marshal.dump($!, ps[1])
79
- ps[1].flush
80
- end
81
- ps[1].close unless (ps[1].closed?)
82
- exit!
83
- }
84
- ensure
85
- $VERBOSE = verbose
86
- end
87
-
88
- pw[0].close
89
- pr[1].close
90
- pe[1].close
91
- ps[1].close
92
-
93
- exc = nil
94
- begin
95
- exc = Marshal.load(ps[0])
96
- rescue EOFError # If we get an EOF error, then the exec was successful
97
- end
98
-
99
- if (not (exc.nil?)) then
100
- raise exc
101
- end
102
-
103
- # PROCESSES[cid] = true
104
-
105
- pi = [pw[1], pr[0], pe[0]]
106
- pw[1].sync = true
107
- if defined? yield
108
- begin
109
- yield(cid, *pi)
110
- return((Process.waitpid2(cid, 1)).last)
111
- ensure
112
- pi.each{|p| p.close unless p.closed?}
113
- end
114
- end
115
- [cid, pw[1], pr[0], pe[0]]
116
- #--}}}
117
- end
118
-
119
- alias open4 popen4
120
- module_function :popen4
121
- module_function :open4
122
-
123
- #--}}}
124
- end
125
-
126
- if $0 == __FILE__
127
- status = Open4::popen4('sh'){|cid,i,o,e|i.puts 'echo 42';i.close; puts o.read;}
128
- p [status]
129
- p status.exitstatus
130
- p status == 0
131
- end