open4 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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