open4 0.2.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 +110 -0
- data/gemspec.rb +23 -0
- data/install +201 -0
- data/install.rb +201 -0
- data/lib/open4-0.2.0.rb +131 -0
- data/lib/open4.rb +131 -0
- data/open4-0.2.0.gem +0 -0
- data/sample/block.rb +19 -0
- data/sample/exception.rb +3 -0
- data/sample/simple.rb +15 -0
- metadata +50 -0
data/README
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
URIS
|
2
|
+
|
3
|
+
http://rubyforge.org/frs/?group_id=1024
|
4
|
+
http://www.codeforpeople.com/lib/ruby/
|
5
|
+
|
6
|
+
|
7
|
+
SYNOPSIS
|
8
|
+
|
9
|
+
open child process with handles on pid, stdin, stdout, and stderr
|
10
|
+
|
11
|
+
|
12
|
+
HISTORY
|
13
|
+
|
14
|
+
0.2.0 :
|
15
|
+
- added exception marshaled from child -> parent when exec fails. thanks
|
16
|
+
to jordan breeding for a patch (yay!) and paul brannan for this most
|
17
|
+
excellent idea.
|
18
|
+
|
19
|
+
0.1.0 :
|
20
|
+
- fixed docs to correctly show return value of popen4 (pid first not last).
|
21
|
+
thanks Stefanie Tellex <stefie10@alum.mit.edu> for catching this.
|
22
|
+
0.0.0 :
|
23
|
+
- initial version
|
24
|
+
|
25
|
+
|
26
|
+
INSTALL
|
27
|
+
|
28
|
+
~> gem install open4
|
29
|
+
|
30
|
+
|
31
|
+
SAMPLES
|
32
|
+
|
33
|
+
simple usage:
|
34
|
+
|
35
|
+
jib:~/eg/ruby/open4/open4-0.2.0 > cat sample/simple.rb
|
36
|
+
require "open4"
|
37
|
+
|
38
|
+
pid, stdin, stdout, stderr = Open4::popen4 "sh"
|
39
|
+
|
40
|
+
stdin.puts "echo 42.out"
|
41
|
+
stdin.puts "echo 42.err 1>&2"
|
42
|
+
stdin.close
|
43
|
+
|
44
|
+
ignored, status = Process::waitpid2 pid
|
45
|
+
|
46
|
+
puts "pid : #{ pid }"
|
47
|
+
puts "stdout : #{ stdout.read.strip }"
|
48
|
+
puts "stderr : #{ stderr.read.strip }"
|
49
|
+
puts "status : #{ status.inspect }"
|
50
|
+
puts "exitstatus : #{ status.exitstatus }"
|
51
|
+
|
52
|
+
|
53
|
+
jib:~/eg/ruby/open4/open4-0.2.0 > ruby sample/simple.rb
|
54
|
+
pid : 17273
|
55
|
+
stdout : 42.out
|
56
|
+
stderr : 42.err
|
57
|
+
status : #<Process::Status: pid=17273,exited(0)>
|
58
|
+
exitstatus : 0
|
59
|
+
|
60
|
+
|
61
|
+
block form - child process is automatically waited for:
|
62
|
+
|
63
|
+
|
64
|
+
jib:~/eg/ruby/open4/open4-0.2.0 > cat sample/block.rb
|
65
|
+
require 'open4'
|
66
|
+
|
67
|
+
status =
|
68
|
+
Open4::popen4("sh") do |pid, stdin, stdout, stderr|
|
69
|
+
stdin.puts "echo 42.out"
|
70
|
+
stdin.puts "echo 42.err 1>&2"
|
71
|
+
stdin.close
|
72
|
+
|
73
|
+
puts "pid : #{ pid }"
|
74
|
+
puts "stdout : #{ stdout.read.strip }"
|
75
|
+
puts "stderr : #{ stderr.read.strip }"
|
76
|
+
end
|
77
|
+
|
78
|
+
puts "status : #{ status.inspect }"
|
79
|
+
puts "exitstatus : #{ status.exitstatus }"
|
80
|
+
|
81
|
+
|
82
|
+
jib:~/eg/ruby/open4/open4-0.2.0 > ruby sample/block.rb
|
83
|
+
pid : 17295
|
84
|
+
stdout : 42.out
|
85
|
+
stderr : 42.err
|
86
|
+
status : #<Process::Status: pid=17295,exited(0)>
|
87
|
+
exitstatus : 0
|
88
|
+
|
89
|
+
|
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
|
94
|
+
require "open4"
|
95
|
+
Open4::popen4 "noexist"
|
96
|
+
|
97
|
+
|
98
|
+
jib:~/eg/ruby/open4/open4-0.2.0 > ruby sample/exception.rb
|
99
|
+
/dmsp/reference/ruby-1.8.1//lib/ruby/site_ruby/open4.rb:100:in `popen4': No such file or directory - noexist (Errno::ENOENT)
|
100
|
+
from sample/exception.rb:3
|
101
|
+
|
102
|
+
|
103
|
+
AUTHOR
|
104
|
+
|
105
|
+
ara.t.howard@noaa.gov
|
106
|
+
|
107
|
+
|
108
|
+
LICENSE
|
109
|
+
|
110
|
+
ruby's
|
data/gemspec.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
lib, version = File::basename(File::dirname(File::expand_path(__FILE__))).split %r/-/, 2
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
Gem::Specification::new do |spec|
|
6
|
+
spec.name = lib
|
7
|
+
spec.version = version
|
8
|
+
spec.platform = Gem::Platform::RUBY
|
9
|
+
spec.summary = lib
|
10
|
+
|
11
|
+
# spec.files = Dir[ File::join("{lib,bin}", "*") ]
|
12
|
+
# spec.require_path = "lib"
|
13
|
+
|
14
|
+
spec.files = Dir::glob "**/**"
|
15
|
+
spec.executables = Dir::glob("bin/*").map{|exe| File::basename exe}
|
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
ADDED
@@ -0,0 +1,201 @@
|
|
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
|
data/install.rb
ADDED
@@ -0,0 +1,201 @@
|
|
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
|
data/lib/open4-0.2.0.rb
ADDED
@@ -0,0 +1,131 @@
|
|
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
|
data/lib/open4.rb
ADDED
@@ -0,0 +1,131 @@
|
|
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
|
data/open4-0.2.0.gem
ADDED
File without changes
|
data/sample/block.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'open4'
|
2
|
+
#
|
3
|
+
# when using block form the child process is automatically waited using
|
4
|
+
# waitpid2
|
5
|
+
#
|
6
|
+
|
7
|
+
status =
|
8
|
+
Open4::popen4("sh") do |pid, stdin, stdout, stderr|
|
9
|
+
stdin.puts "echo 42.out"
|
10
|
+
stdin.puts "echo 42.err 1>&2"
|
11
|
+
stdin.close
|
12
|
+
|
13
|
+
puts "pid : #{ pid }"
|
14
|
+
puts "stdout : #{ stdout.read.strip }"
|
15
|
+
puts "stderr : #{ stderr.read.strip }"
|
16
|
+
end
|
17
|
+
|
18
|
+
puts "status : #{ status.inspect }"
|
19
|
+
puts "exitstatus : #{ status.exitstatus }"
|
data/sample/exception.rb
ADDED
data/sample/simple.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "open4"
|
2
|
+
|
3
|
+
pid, stdin, stdout, stderr = Open4::popen4 "sh"
|
4
|
+
|
5
|
+
stdin.puts "echo 42.out"
|
6
|
+
stdin.puts "echo 42.err 1>&2"
|
7
|
+
stdin.close
|
8
|
+
|
9
|
+
ignored, status = Process::waitpid2 pid
|
10
|
+
|
11
|
+
puts "pid : #{ pid }"
|
12
|
+
puts "stdout : #{ stdout.read.strip }"
|
13
|
+
puts "stderr : #{ stderr.read.strip }"
|
14
|
+
puts "status : #{ status.inspect }"
|
15
|
+
puts "exitstatus : #{ status.exitstatus }"
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: open4
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.2.0
|
7
|
+
date: 2006-02-13 00:00:00.000000 -07: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:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: false
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
-
|
22
|
+
- ">"
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.0.0
|
25
|
+
version:
|
26
|
+
platform: ruby
|
27
|
+
signing_key:
|
28
|
+
cert_chain:
|
29
|
+
authors:
|
30
|
+
- Ara T. Howard
|
31
|
+
files:
|
32
|
+
- lib
|
33
|
+
- install
|
34
|
+
- install.rb
|
35
|
+
- README
|
36
|
+
- sample
|
37
|
+
- gemspec.rb
|
38
|
+
- open4-0.2.0.gem
|
39
|
+
- lib/open4.rb
|
40
|
+
- lib/open4-0.2.0.rb
|
41
|
+
- sample/block.rb
|
42
|
+
- sample/simple.rb
|
43
|
+
- sample/exception.rb
|
44
|
+
test_files: []
|
45
|
+
rdoc_options: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
executables: []
|
48
|
+
extensions: []
|
49
|
+
requirements: []
|
50
|
+
dependencies: []
|