open3 0.1.1 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f598d530ced19be109f452253e266841b9c00d71e96687d31c2e5fa5db86957
4
- data.tar.gz: be2ec8a86eeaa8e60907e9c95268d6b65ef3b477083051054a6adb1521a0fceb
3
+ metadata.gz: d381ba178ac3fc0539b0f526890e2951b8e6f0332d294cd4930b895837643b3b
4
+ data.tar.gz: 7435b46d29c7e24231af4adb8106a32cde9850a896e61153a08fe4b519d5645b
5
5
  SHA512:
6
- metadata.gz: c327ed7dd6b0769dd17809b7b155028ea6604364ce6a123b5f59d7f9a98ffe4e078e170f0268fb5eccdfc96a10bf9d284bdd85391052b104469906d410e010e9
7
- data.tar.gz: b641d417656c67cd4199a0592ef71dc24d852fda7084cf31b89e4172eebf91e660f585a3213ab8257cfa942fa928be5ee7fc8dd2b693a76605b0f9acd38f1033
6
+ metadata.gz: e8770606eb547891a3c87fd8636c7a911189d940aa332909098fff948660978c717bb3bd2727fb13378e6b10ee207b7cee17f91a1b02ef18a20cded2bf76f3a9
7
+ data.tar.gz: b12409782b071af20bc628579d5a03ea89023c5e3dd1acbae90a67bafbb748d5c7a4de46ecc5d9ddc2da020de5ff53b89448719025706fc08dfe33b87563df83
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: 'github-actions'
4
+ directory: '/'
5
+ schedule:
6
+ interval: 'weekly'
@@ -0,0 +1,21 @@
1
+ name: test
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ name: build (${{ matrix.ruby }} / ${{ matrix.os }})
8
+ strategy:
9
+ matrix:
10
+ ruby: [ 2.7, 2.6, head, jruby-9.3 ]
11
+ os: [ ubuntu-latest, macos-latest ]
12
+ runs-on: ${{ matrix.os }}
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - name: Set up Ruby
16
+ uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: ${{ matrix.ruby }}
19
+ bundler-cache: true # 'bundle install' and cache
20
+ - name: Run test
21
+ run: bundle exec rake test
@@ -3,22 +3,26 @@ name: test
3
3
  on: [push, pull_request]
4
4
 
5
5
  jobs:
6
- build:
6
+ ruby-versions:
7
+ uses: ruby/actions/.github/workflows/ruby_versions.yml@master
8
+ with:
9
+ engine: cruby
10
+ min_version: 2.6
11
+
12
+ test:
13
+ needs: ruby-versions
7
14
  name: build (${{ matrix.ruby }} / ${{ matrix.os }})
8
15
  strategy:
9
16
  matrix:
10
- ruby: [ 2.7, 2.6, head ]
17
+ ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
11
18
  os: [ ubuntu-latest, macos-latest ]
12
19
  runs-on: ${{ matrix.os }}
13
20
  steps:
14
- - uses: actions/checkout@master
21
+ - uses: actions/checkout@v4
15
22
  - name: Set up Ruby
16
23
  uses: ruby/setup-ruby@v1
17
24
  with:
18
25
  ruby-version: ${{ matrix.ruby }}
19
- - name: Install dependencies
20
- run: |
21
- gem install bundler --no-document
22
- bundle install
26
+ bundler-cache: true # 'bundle install' and cache
23
27
  - name: Run test
24
- run: rake test
28
+ run: bundle exec rake test
data/Gemfile CHANGED
@@ -6,4 +6,5 @@ group :development do
6
6
  gem "bundler"
7
7
  gem "rake"
8
8
  gem "test-unit"
9
+ gem "test-unit-ruby-core"
9
10
  end
data/Rakefile CHANGED
@@ -7,11 +7,4 @@ Rake::TestTask.new(:test) do |t|
7
7
  t.test_files = FileList["test/**/test_*.rb"]
8
8
  end
9
9
 
10
- task :sync_tool do
11
- require 'fileutils'
12
- FileUtils.cp "../ruby/tool/lib/test/unit/core_assertions.rb", "./test/lib"
13
- FileUtils.cp "../ruby/tool/lib/envutil.rb", "./test/lib"
14
- FileUtils.cp "../ruby/tool/lib/find_executable.rb", "./test/lib"
15
- end
16
-
17
10
  task :default => :test
@@ -0,0 +1,127 @@
1
+ #
2
+ # Custom implementation of Open3.popen{3,2,2e} that uses java.lang.ProcessBuilder rather than pipes and spawns.
3
+ #
4
+
5
+ require 'jruby' # need access to runtime for RubyStatus construction
6
+
7
+ module Open3
8
+
9
+ java_import java.lang.ProcessBuilder
10
+ java_import org.jruby.RubyProcess
11
+ java_import org.jruby.util.ShellLauncher
12
+
13
+ def popen3(*cmd, &block)
14
+ if cmd.size > 0 && Hash === cmd[-1]
15
+ opts = cmd.pop
16
+ else
17
+ opts = {}
18
+ end
19
+ processbuilder_run(cmd, opts, io: IO_3, &block)
20
+ end
21
+ module_function :popen3
22
+
23
+ IO_3 = proc do |process|
24
+ [process.getOutputStream.to_io, process.getInputStream.to_io, process.getErrorStream.to_io]
25
+ end
26
+
27
+ BUILD_2 = proc do |builder|
28
+ builder.redirectError(ProcessBuilder::Redirect::INHERIT)
29
+ end
30
+
31
+ IO_2 = proc do |process|
32
+ [process.getOutputStream.to_io, process.getInputStream.to_io]
33
+ end
34
+
35
+ def popen2(*cmd, &block)
36
+ if cmd.size > 0 && Hash === cmd[-1]
37
+ opts = cmd.pop
38
+ else
39
+ opts = {}
40
+ end
41
+ processbuilder_run(cmd, opts, build: BUILD_2, io: IO_2, &block)
42
+ end
43
+ module_function :popen2
44
+
45
+ BUILD_2E = proc do |builder|
46
+ builder.redirectErrorStream(true)
47
+ end
48
+
49
+ def popen2e(*cmd, &block)
50
+ if cmd.size > 0 && Hash === cmd[-1]
51
+ opts = cmd.pop
52
+ else
53
+ opts = {}
54
+ end
55
+ processbuilder_run(cmd, opts, build: BUILD_2E, io: IO_2, &block)
56
+ end
57
+ module_function :popen2e
58
+
59
+ def processbuilder_run(cmd, opts, build: nil, io:)
60
+ opts.each do |k, v|
61
+ if Integer === k
62
+ if IO == v || !(String === v || v.respond_to?(:to_path))
63
+ # target is an open IO or a non-pathable object, bail out
64
+ raise NotImplementedError.new("redirect to an open IO is not implemented on this platform")
65
+ end
66
+ end
67
+ end
68
+
69
+ if Hash === cmd[0]
70
+ env = cmd.shift;
71
+ else
72
+ env = {}
73
+ end
74
+
75
+ if cmd.size == 1 && (cmd[0] =~ / / || ShellLauncher.shouldUseShell(cmd[0]))
76
+ cmd = [RbConfig::CONFIG['SHELL'], JRuby::Util::ON_WINDOWS ? '/c' : '-c', cmd[0]]
77
+ end
78
+
79
+ builder = ProcessBuilder.new(cmd.to_java(:string))
80
+
81
+ builder.directory(java.io.File.new(opts[:chdir] || Dir.pwd))
82
+
83
+ environment = builder.environment
84
+ env.each { |k, v| v.nil? ? environment.remove(k) : environment.put(k, v) }
85
+
86
+ build.call(builder) if build
87
+
88
+ process = builder.start
89
+
90
+ pid = org.jruby.util.ShellLauncher.getPidFromProcess(process)
91
+
92
+ parent_io = io.call(process)
93
+
94
+ parent_io.each {|i| i.sync = true}
95
+
96
+ wait_thr = DetachThread.new(pid) { RubyProcess::RubyStatus.newProcessStatus(JRuby.runtime, process.waitFor << 8, pid) }
97
+
98
+ result = [*parent_io, wait_thr]
99
+
100
+ if defined? yield
101
+ begin
102
+ return yield(*result)
103
+ ensure
104
+ parent_io.each(&:close)
105
+ wait_thr.join
106
+ end
107
+ end
108
+
109
+ result
110
+ end
111
+ module_function :processbuilder_run
112
+ class << self
113
+ private :processbuilder_run
114
+ end
115
+
116
+ class DetachThread < Thread
117
+ attr_reader :pid
118
+
119
+ def initialize(pid)
120
+ super
121
+
122
+ @pid = pid
123
+ self[:pid] = pid
124
+ end
125
+ end
126
+
127
+ end
@@ -0,0 +1,3 @@
1
+ module Open3
2
+ VERSION = "0.2.0"
3
+ end
data/lib/open3.rb CHANGED
@@ -29,58 +29,137 @@
29
29
  # - Open3.pipeline : run a pipeline and wait for its completion
30
30
  #
31
31
 
32
+ require 'open3/version'
33
+
32
34
  module Open3
33
- VERSION = "0.1.1"
34
35
 
35
- # Open stdin, stdout, and stderr streams and start external executable.
36
- # In addition, a thread to wait for the started process is created.
37
- # The thread has a pid method and a thread variable :pid which is the pid of
38
- # the started process.
36
+ # :call-seq:
37
+ # Open3.popen3([env, ] command_line, options = {}) -> [stdin, stdout, stderr, wait_thread]
38
+ # Open3.popen3([env, ] exe_path, *args, options = {}) -> [stdin, stdout, stderr, wait_thread]
39
+ # Open3.popen3([env, ] command_line, options = {}) {|stdin, stdout, stderr, wait_thread| ... } -> object
40
+ # Open3.popen3([env, ] exe_path, *args, options = {}) {|stdin, stdout, stderr, wait_thread| ... } -> object
41
+ #
42
+ # Basically a wrapper for Process.spawn that:
43
+ #
44
+ # - Creates a child process, by calling Process.spawn with the given arguments.
45
+ # - Creates streams +stdin+, +stdout+, and +stderr+,
46
+ # which are the standard input, standard output, and standard error streams
47
+ # in the child process.
48
+ # - Creates thread +wait_thread+ that waits for the child process to exit;
49
+ # the thread has method +pid+, which returns the process ID
50
+ # of the child process.
51
+ #
52
+ # With no block given, returns the array
53
+ # <tt>[stdin, stdout, stderr, wait_thread]</tt>.
54
+ # The caller should close each of the three returned streams.
55
+ #
56
+ # stdin, stdout, stderr, wait_thread = Open3.popen3('echo')
57
+ # # => [#<IO:fd 8>, #<IO:fd 10>, #<IO:fd 12>, #<Process::Waiter:0x00007f58d5428f58 run>]
58
+ # stdin.close
59
+ # stdout.close
60
+ # stderr.close
61
+ # wait_thread.pid # => 2210481
62
+ # wait_thread.value # => #<Process::Status: pid 2210481 exit 0>
63
+ #
64
+ # With a block given, calls the block with the four variables
65
+ # (three streams and the wait thread)
66
+ # and returns the block's return value.
67
+ # The caller need not close the streams:
68
+ #
69
+ # Open3.popen3('echo') do |stdin, stdout, stderr, wait_thread|
70
+ # p stdin
71
+ # p stdout
72
+ # p stderr
73
+ # p wait_thread
74
+ # p wait_thread.pid
75
+ # p wait_thread.value
76
+ # end
39
77
  #
40
- # Block form:
78
+ # Output:
41
79
  #
42
- # Open3.popen3([env,] cmd... [, opts]) {|stdin, stdout, stderr, wait_thr|
43
- # pid = wait_thr.pid # pid of the started process.
44
- # ...
45
- # exit_status = wait_thr.value # Process::Status object returned.
46
- # }
80
+ # #<IO:fd 6>
81
+ # #<IO:fd 7>
82
+ # #<IO:fd 9>
83
+ # #<Process::Waiter:0x00007f58d53606e8 sleep>
84
+ # 2211047
85
+ # #<Process::Status: pid 2211047 exit 0>
47
86
  #
48
- # Non-block form:
87
+ # Like Process.spawn, this method has potential security vulnerabilities
88
+ # if called with untrusted input;
89
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc].
49
90
  #
50
- # stdin, stdout, stderr, wait_thr = Open3.popen3([env,] cmd... [, opts])
51
- # pid = wait_thr[:pid] # pid of the started process
52
- # ...
53
- # stdin.close # stdin, stdout and stderr should be closed explicitly in this form.
54
- # stdout.close
55
- # stderr.close
56
- # exit_status = wait_thr.value # Process::Status object returned.
91
+ # Unlike Process.spawn, this method waits for the child process to exit
92
+ # before returning, so the caller need not do so.
57
93
  #
58
- # The parameters env, cmd, and opts are passed to Process.spawn.
59
- # A commandline string and a list of argument strings can be accepted as follows:
94
+ # Argument +options+ is a hash of options for the new process;
95
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
60
96
  #
61
- # Open3.popen3("echo abc") {|i, o, e, t| ... }
62
- # Open3.popen3("echo", "abc") {|i, o, e, t| ... }
63
- # Open3.popen3(["echo", "argv0"], "abc") {|i, o, e, t| ... }
97
+ # The single required argument is one of the following:
64
98
  #
65
- # If the last parameter, opts, is a Hash, it is recognized as an option for Process.spawn.
99
+ # - +command_line+ if it is a string,
100
+ # and if it begins with a shell reserved word or special built-in,
101
+ # or if it contains one or more metacharacters.
102
+ # - +exe_path+ otherwise.
66
103
  #
67
- # Open3.popen3("pwd", :chdir=>"/") {|i,o,e,t|
68
- # p o.read.chomp #=> "/"
69
- # }
104
+ # <b>Argument +command_line+</b>
105
+ #
106
+ # \String argument +command_line+ is a command line to be passed to a shell;
107
+ # it must begin with a shell reserved word, begin with a special built-in,
108
+ # or contain meta characters:
109
+ #
110
+ # Open3.popen3('if true; then echo "Foo"; fi') {|*args| p args } # Shell reserved word.
111
+ # Open3.popen3('echo') {|*args| p args } # Built-in.
112
+ # Open3.popen3('date > date.tmp') {|*args| p args } # Contains meta character.
113
+ #
114
+ # Output (similar for each call above):
115
+ #
116
+ # [#<IO:(closed)>, #<IO:(closed)>, #<IO:(closed)>, #<Process::Waiter:0x00007f58d52f28c8 dead>]
117
+ #
118
+ # The command line may also contain arguments and options for the command:
119
+ #
120
+ # Open3.popen3('echo "Foo"') { |i, o, e, t| o.gets }
121
+ # "Foo\n"
122
+ #
123
+ # <b>Argument +exe_path+</b>
124
+ #
125
+ # Argument +exe_path+ is one of the following:
126
+ #
127
+ # - The string path to an executable to be called.
128
+ # - A 2-element array containing the path to an executable
129
+ # and the string to be used as the name of the executing process.
70
130
  #
71
- # wait_thr.value waits for the termination of the process.
72
- # The block form also waits for the process when it returns.
131
+ # Example:
132
+ #
133
+ # Open3.popen3('/usr/bin/date') { |i, o, e, t| o.gets }
134
+ # # => "Wed Sep 27 02:56:44 PM CDT 2023\n"
135
+ #
136
+ # Ruby invokes the executable directly, with no shell and no shell expansion:
137
+ #
138
+ # Open3.popen3('doesnt_exist') { |i, o, e, t| o.gets } # Raises Errno::ENOENT
139
+ #
140
+ # If one or more +args+ is given, each is an argument or option
141
+ # to be passed to the executable:
73
142
  #
74
- # Closing stdin, stdout and stderr does not wait for the process to complete.
143
+ # Open3.popen3('echo', 'C #') { |i, o, e, t| o.gets }
144
+ # # => "C #\n"
145
+ # Open3.popen3('echo', 'hello', 'world') { |i, o, e, t| o.gets }
146
+ # # => "hello world\n"
75
147
  #
76
- # You should be careful to avoid deadlocks.
77
- # Since pipes are fixed length buffers,
78
- # Open3.popen3("prog") {|i, o, e, t| o.read } deadlocks if
79
- # the program generates too much output on stderr.
80
- # You should read stdout and stderr simultaneously (using threads or IO.select).
81
- # However, if you don't need stderr output, you can use Open3.popen2.
82
- # If merged stdout and stderr output is not a problem, you can use Open3.popen2e.
83
- # If you really need stdout and stderr output as separate strings, you can consider Open3.capture3.
148
+ # Take care to avoid deadlocks.
149
+ # Output streams +stdout+ and +stderr+ have fixed-size buffers,
150
+ # so reading extensively from one but not the other can cause a deadlock
151
+ # when the unread buffer fills.
152
+ # To avoid that, +stdout+ and +stderr+ should be read simultaneously
153
+ # (using threads or IO.select).
154
+ #
155
+ # Related:
156
+ #
157
+ # - Open3.popen2: Makes the standard input and standard output streams
158
+ # of the child process available as separate streams,
159
+ # with no access to the standard error stream.
160
+ # - Open3.popen2e: Makes the standard input and the merge
161
+ # of the standard output and standard error streams
162
+ # of the child process available as separate streams.
84
163
  #
85
164
  def popen3(*cmd, &block)
86
165
  if Hash === cmd.last
@@ -103,45 +182,124 @@ module Open3
103
182
  end
104
183
  module_function :popen3
105
184
 
106
- # Open3.popen2 is similar to Open3.popen3 except that it doesn't create a pipe for
107
- # the standard error stream.
185
+ # :call-seq:
186
+ # Open3.popen2([env, ] command_line, options = {}) -> [stdin, stdout, wait_thread]
187
+ # Open3.popen2([env, ] exe_path, *args, options = {}) -> [stdin, stdout, wait_thread]
188
+ # Open3.popen2([env, ] command_line, options = {}) {|stdin, stdout, wait_thread| ... } -> object
189
+ # Open3.popen2([env, ] exe_path, *args, options = {}) {|stdin, stdout, wait_thread| ... } -> object
190
+ #
191
+ # Basically a wrapper for Process.spawn that:
192
+ #
193
+ # - Creates a child process, by calling Process.spawn with the given arguments.
194
+ # - Creates streams +stdin+ and +stdout+,
195
+ # which are the standard input and standard output streams
196
+ # in the child process.
197
+ # - Creates thread +wait_thread+ that waits for the child process to exit;
198
+ # the thread has method +pid+, which returns the process ID
199
+ # of the child process.
200
+ #
201
+ # With no block given, returns the array
202
+ # <tt>[stdin, stdout, wait_thread]</tt>.
203
+ # The caller should close each of the two returned streams.
204
+ #
205
+ # stdin, stdout, wait_thread = Open3.popen2('echo')
206
+ # # => [#<IO:fd 6>, #<IO:fd 7>, #<Process::Waiter:0x00007f58d52dbe98 run>]
207
+ # stdin.close
208
+ # stdout.close
209
+ # wait_thread.pid # => 2263572
210
+ # wait_thread.value # => #<Process::Status: pid 2263572 exit 0>
211
+ #
212
+ # With a block given, calls the block with the three variables
213
+ # (two streams and the wait thread)
214
+ # and returns the block's return value.
215
+ # The caller need not close the streams:
216
+ #
217
+ # Open3.popen2('echo') do |stdin, stdout, wait_thread|
218
+ # p stdin
219
+ # p stdout
220
+ # p wait_thread
221
+ # p wait_thread.pid
222
+ # p wait_thread.value
223
+ # end
108
224
  #
109
- # Block form:
225
+ # Output:
110
226
  #
111
- # Open3.popen2([env,] cmd... [, opts]) {|stdin, stdout, wait_thr|
112
- # pid = wait_thr.pid # pid of the started process.
113
- # ...
114
- # exit_status = wait_thr.value # Process::Status object returned.
115
- # }
227
+ # #<IO:fd 6>
228
+ # #<IO:fd 7>
229
+ # #<Process::Waiter:0x00007f58d59a34b0 sleep>
230
+ # 2263636
231
+ # #<Process::Status: pid 2263636 exit 0>
116
232
  #
117
- # Non-block form:
233
+ # Like Process.spawn, this method has potential security vulnerabilities
234
+ # if called with untrusted input;
235
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc].
118
236
  #
119
- # stdin, stdout, wait_thr = Open3.popen2([env,] cmd... [, opts])
120
- # ...
121
- # stdin.close # stdin and stdout should be closed explicitly in this form.
122
- # stdout.close
237
+ # Unlike Process.spawn, this method waits for the child process to exit
238
+ # before returning, so the caller need not do so.
239
+ #
240
+ # Argument +options+ is a hash of options for the new process;
241
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
242
+ #
243
+ # The single required argument is one of the following:
244
+ #
245
+ # - +command_line+ if it is a string,
246
+ # and if it begins with a shell reserved word or special built-in,
247
+ # or if it contains one or more metacharacters.
248
+ # - +exe_path+ otherwise.
249
+ #
250
+ # <b>Argument +command_line+</b>
251
+ #
252
+ # \String argument +command_line+ is a command line to be passed to a shell;
253
+ # it must begin with a shell reserved word, begin with a special built-in,
254
+ # or contain meta characters:
255
+ #
256
+ # Open3.popen2('if true; then echo "Foo"; fi') {|*args| p args } # Shell reserved word.
257
+ # Open3.popen2('echo') {|*args| p args } # Built-in.
258
+ # Open3.popen2('date > date.tmp') {|*args| p args } # Contains meta character.
123
259
  #
124
- # See Process.spawn for the optional hash arguments _env_ and _opts_.
260
+ # Output (similar for each call above):
261
+ #
262
+ # # => [#<IO:(closed)>, #<IO:(closed)>, #<Process::Waiter:0x00007f7577dfe410 dead>]
263
+ #
264
+ # The command line may also contain arguments and options for the command:
265
+ #
266
+ # Open3.popen2('echo "Foo"') { |i, o, t| o.gets }
267
+ # "Foo\n"
268
+ #
269
+ # <b>Argument +exe_path+</b>
270
+ #
271
+ # Argument +exe_path+ is one of the following:
272
+ #
273
+ # - The string path to an executable to be called.
274
+ # - A 2-element array containing the path to an executable
275
+ # and the string to be used as the name of the executing process.
125
276
  #
126
277
  # Example:
127
278
  #
128
- # Open3.popen2("wc -c") {|i,o,t|
129
- # i.print "answer to life the universe and everything"
130
- # i.close
131
- # p o.gets #=> "42\n"
132
- # }
279
+ # Open3.popen2('/usr/bin/date') { |i, o, t| o.gets }
280
+ # # => "Thu Sep 28 09:41:06 AM CDT 2023\n"
133
281
  #
134
- # Open3.popen2("bc -q") {|i,o,t|
135
- # i.puts "obase=13"
136
- # i.puts "6 * 9"
137
- # p o.gets #=> "42\n"
138
- # }
282
+ # Ruby invokes the executable directly, with no shell and no shell expansion:
139
283
  #
140
- # Open3.popen2("dc") {|i,o,t|
141
- # i.print "42P"
142
- # i.close
143
- # p o.read #=> "*"
144
- # }
284
+ # Open3.popen2('doesnt_exist') { |i, o, t| o.gets } # Raises Errno::ENOENT
285
+ #
286
+ # If one or more +args+ is given, each is an argument or option
287
+ # to be passed to the executable:
288
+ #
289
+ # Open3.popen2('echo', 'C #') { |i, o, t| o.gets }
290
+ # # => "C #\n"
291
+ # Open3.popen2('echo', 'hello', 'world') { |i, o, t| o.gets }
292
+ # # => "hello world\n"
293
+ #
294
+ #
295
+ # Related:
296
+ #
297
+ # - Open3.popen2e: Makes the standard input and the merge
298
+ # of the standard output and standard error streams
299
+ # of the child process available as separate streams.
300
+ # - Open3.popen3: Makes the standard input, standard output,
301
+ # and standard error streams
302
+ # of the child process available as separate streams.
145
303
  #
146
304
  def popen2(*cmd, &block)
147
305
  if Hash === cmd.last
@@ -161,36 +319,123 @@ module Open3
161
319
  end
162
320
  module_function :popen2
163
321
 
164
- # Open3.popen2e is similar to Open3.popen3 except that it merges
165
- # the standard output stream and the standard error stream.
322
+ # :call-seq:
323
+ # Open3.popen2e([env, ] command_line, options = {}) -> [stdin, stdout_and_stderr, wait_thread]
324
+ # Open3.popen2e([env, ] exe_path, *args, options = {}) -> [stdin, stdout_and_stderr, wait_thread]
325
+ # Open3.popen2e([env, ] command_line, options = {}) {|stdin, stdout_and_stderr, wait_thread| ... } -> object
326
+ # Open3.popen2e([env, ] exe_path, *args, options = {}) {|stdin, stdout_and_stderr, wait_thread| ... } -> object
327
+ #
328
+ # Basically a wrapper for Process.spawn that:
329
+ #
330
+ # - Creates a child process, by calling Process.spawn with the given arguments.
331
+ # - Creates streams +stdin+, +stdout_and_stderr+,
332
+ # which are the standard input and the merge of the standard output
333
+ # and standard error streams in the child process.
334
+ # - Creates thread +wait_thread+ that waits for the child process to exit;
335
+ # the thread has method +pid+, which returns the process ID
336
+ # of the child process.
337
+ #
338
+ # With no block given, returns the array
339
+ # <tt>[stdin, stdout_and_stderr, wait_thread]</tt>.
340
+ # The caller should close each of the two returned streams.
341
+ #
342
+ # stdin, stdout_and_stderr, wait_thread = Open3.popen2e('echo')
343
+ # # => [#<IO:fd 6>, #<IO:fd 7>, #<Process::Waiter:0x00007f7577da4398 run>]
344
+ # stdin.close
345
+ # stdout_and_stderr.close
346
+ # wait_thread.pid # => 2274600
347
+ # wait_thread.value # => #<Process::Status: pid 2274600 exit 0>
348
+ #
349
+ # With a block given, calls the block with the three variables
350
+ # (two streams and the wait thread)
351
+ # and returns the block's return value.
352
+ # The caller need not close the streams:
353
+ #
354
+ # Open3.popen2e('echo') do |stdin, stdout_and_stderr, wait_thread|
355
+ # p stdin
356
+ # p stdout_and_stderr
357
+ # p wait_thread
358
+ # p wait_thread.pid
359
+ # p wait_thread.value
360
+ # end
166
361
  #
167
- # Block form:
362
+ # Output:
168
363
  #
169
- # Open3.popen2e([env,] cmd... [, opts]) {|stdin, stdout_and_stderr, wait_thr|
170
- # pid = wait_thr.pid # pid of the started process.
171
- # ...
172
- # exit_status = wait_thr.value # Process::Status object returned.
173
- # }
364
+ # #<IO:fd 6>
365
+ # #<IO:fd 7>
366
+ # #<Process::Waiter:0x00007f75777578c8 sleep>
367
+ # 2274763
368
+ # #<Process::Status: pid 2274763 exit 0>
174
369
  #
175
- # Non-block form:
370
+ # Like Process.spawn, this method has potential security vulnerabilities
371
+ # if called with untrusted input;
372
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc].
176
373
  #
177
- # stdin, stdout_and_stderr, wait_thr = Open3.popen2e([env,] cmd... [, opts])
178
- # ...
179
- # stdin.close # stdin and stdout_and_stderr should be closed explicitly in this form.
180
- # stdout_and_stderr.close
374
+ # Unlike Process.spawn, this method waits for the child process to exit
375
+ # before returning, so the caller need not do so.
376
+ #
377
+ # Argument +options+ is a hash of options for the new process;
378
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
379
+ #
380
+ # The single required argument is one of the following:
381
+ #
382
+ # - +command_line+ if it is a string,
383
+ # and if it begins with a shell reserved word or special built-in,
384
+ # or if it contains one or more metacharacters.
385
+ # - +exe_path+ otherwise.
386
+ #
387
+ # <b>Argument +command_line+</b>
388
+ #
389
+ # \String argument +command_line+ is a command line to be passed to a shell;
390
+ # it must begin with a shell reserved word, begin with a special built-in,
391
+ # or contain meta characters:
181
392
  #
182
- # See Process.spawn for the optional hash arguments _env_ and _opts_.
393
+ # Open3.popen2e('if true; then echo "Foo"; fi') {|*args| p args } # Shell reserved word.
394
+ # Open3.popen2e('echo') {|*args| p args } # Built-in.
395
+ # Open3.popen2e('date > date.tmp') {|*args| p args } # Contains meta character.
396
+ #
397
+ # Output (similar for each call above):
398
+ #
399
+ # # => [#<IO:(closed)>, #<IO:(closed)>, #<Process::Waiter:0x00007f7577d8a1f0 dead>]
400
+ #
401
+ # The command line may also contain arguments and options for the command:
402
+ #
403
+ # Open3.popen2e('echo "Foo"') { |i, o_and_e, t| o_and_e.gets }
404
+ # "Foo\n"
405
+ #
406
+ # <b>Argument +exe_path+</b>
407
+ #
408
+ # Argument +exe_path+ is one of the following:
409
+ #
410
+ # - The string path to an executable to be called.
411
+ # - A 2-element array containing the path to an executable
412
+ # and the string to be used as the name of the executing process.
183
413
  #
184
414
  # Example:
185
- # # check gcc warnings
186
- # source = "foo.c"
187
- # Open3.popen2e("gcc", "-Wall", source) {|i,oe,t|
188
- # oe.each {|line|
189
- # if /warning/ =~ line
190
- # ...
191
- # end
192
- # }
193
- # }
415
+ #
416
+ # Open3.popen2e('/usr/bin/date') { |i, o_and_e, t| o_and_e.gets }
417
+ # # => "Thu Sep 28 01:58:45 PM CDT 2023\n"
418
+ #
419
+ # Ruby invokes the executable directly, with no shell and no shell expansion:
420
+ #
421
+ # Open3.popen2e('doesnt_exist') { |i, o_and_e, t| o_and_e.gets } # Raises Errno::ENOENT
422
+ #
423
+ # If one or more +args+ is given, each is an argument or option
424
+ # to be passed to the executable:
425
+ #
426
+ # Open3.popen2e('echo', 'C #') { |i, o_and_e, t| o_and_e.gets }
427
+ # # => "C #\n"
428
+ # Open3.popen2e('echo', 'hello', 'world') { |i, o_and_e, t| o_and_e.gets }
429
+ # # => "hello world\n"
430
+ #
431
+ # Related:
432
+ #
433
+ # - Open3.popen2: Makes the standard input and standard output streams
434
+ # of the child process available as separate streams,
435
+ # with no access to the standard error stream.
436
+ # - Open3.popen3: Makes the standard input, standard output,
437
+ # and standard error streams
438
+ # of the child process available as separate streams.
194
439
  #
195
440
  def popen2e(*cmd, &block)
196
441
  if Hash === cmd.last
@@ -237,44 +482,95 @@ module Open3
237
482
  private :popen_run
238
483
  end
239
484
 
240
- # Open3.capture3 captures the standard output and the standard error of a command.
485
+ # :call-seq:
486
+ # Open3.capture3([env, ] command_line, options = {}) -> [stdout_s, stderr_s, status]
487
+ # Open3.capture3([env, ] exe_path, *args, options = {}) -> [stdout_s, stderr_s, status]
241
488
  #
242
- # stdout_str, stderr_str, status = Open3.capture3([env,] cmd... [, opts])
489
+ # Basically a wrapper for Open3.popen3 that:
243
490
  #
244
- # The arguments env, cmd and opts are passed to Open3.popen3 except
245
- # <code>opts[:stdin_data]</code> and <code>opts[:binmode]</code>. See Process.spawn.
491
+ # - Creates a child process, by calling Open3.popen3 with the given arguments
492
+ # (except for certain entries in hash +options+; see below).
493
+ # - Returns as strings +stdout_s+ and +stderr_s+ the standard output
494
+ # and standard error of the child process.
495
+ # - Returns as +status+ a <tt>Process::Status</tt> object
496
+ # that represents the exit status of the child process.
246
497
  #
247
- # If <code>opts[:stdin_data]</code> is specified, it is sent to the command's standard input.
498
+ # Returns the array <tt>[stdout_s, stderr_s, status]</tt>:
248
499
  #
249
- # If <code>opts[:binmode]</code> is true, internal pipes are set to binary mode.
500
+ # stdout_s, stderr_s, status = Open3.capture3('echo "Foo"')
501
+ # # => ["Foo\n", "", #<Process::Status: pid 2281954 exit 0>]
250
502
  #
251
- # Examples:
503
+ # Like Process.spawn, this method has potential security vulnerabilities
504
+ # if called with untrusted input;
505
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc].
252
506
  #
253
- # # dot is a command of graphviz.
254
- # graph = <<'End'
255
- # digraph g {
256
- # a -> b
257
- # }
258
- # End
259
- # drawn_graph, dot_log = Open3.capture3("dot -v", :stdin_data=>graph)
260
- #
261
- # o, e, s = Open3.capture3("echo abc; sort >&2", :stdin_data=>"foo\nbar\nbaz\n")
262
- # p o #=> "abc\n"
263
- # p e #=> "bar\nbaz\nfoo\n"
264
- # p s #=> #<Process::Status: pid 32682 exit 0>
265
- #
266
- # # generate a thumbnail image using the convert command of ImageMagick.
267
- # # However, if the image is really stored in a file,
268
- # # system("convert", "-thumbnail", "80", "png:#{filename}", "png:-") is better
269
- # # because of reduced memory consumption.
270
- # # But if the image is stored in a DB or generated by the gnuplot Open3.capture2 example,
271
- # # Open3.capture3 should be considered.
272
- # #
273
- # image = File.read("/usr/share/openclipart/png/animals/mammals/sheep-md-v0.1.png", :binmode=>true)
274
- # thumbnail, err, s = Open3.capture3("convert -thumbnail 80 png:- png:-", :stdin_data=>image, :binmode=>true)
275
- # if s.success?
276
- # STDOUT.binmode; print thumbnail
277
- # end
507
+ # Unlike Process.spawn, this method waits for the child process to exit
508
+ # before returning, so the caller need not do so.
509
+ #
510
+ # Argument +options+ is a hash of options for the new process;
511
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
512
+ #
513
+ # The hash +options+ is passed to method Open3.popen3;
514
+ # two options have local effect in method Open3.capture3:
515
+ #
516
+ # - If entry <tt>options[:stdin_data]</tt> exists, the entry is removed
517
+ # and its string value is sent to the command's standard input:
518
+ #
519
+ # Open3.capture3('tee', stdin_data: 'Foo')
520
+ # # => ["Foo", "", #<Process::Status: pid 2319575 exit 0>]
521
+ #
522
+ # - If entry <tt>options[:binmode]</tt> exists, the entry is removed
523
+ # the internal streams are set to binary mode.
524
+ #
525
+ # The single required argument is one of the following:
526
+ #
527
+ # - +command_line+ if it is a string,
528
+ # and if it begins with a shell reserved word or special built-in,
529
+ # or if it contains one or more metacharacters.
530
+ # - +exe_path+ otherwise.
531
+ #
532
+ # <b>Argument +command_line+</b>
533
+ #
534
+ # \String argument +command_line+ is a command line to be passed to a shell;
535
+ # it must begin with a shell reserved word, begin with a special built-in,
536
+ # or contain meta characters:
537
+ #
538
+ # Open3.capture3('if true; then echo "Foo"; fi') # Shell reserved word.
539
+ # # => ["Foo\n", "", #<Process::Status: pid 2282025 exit 0>]
540
+ # Open3.capture3('echo') # Built-in.
541
+ # # => ["\n", "", #<Process::Status: pid 2282092 exit 0>]
542
+ # Open3.capture3('date > date.tmp') # Contains meta character.
543
+ # # => ["", "", #<Process::Status: pid 2282110 exit 0>]
544
+ #
545
+ # The command line may also contain arguments and options for the command:
546
+ #
547
+ # Open3.capture3('echo "Foo"')
548
+ # # => ["Foo\n", "", #<Process::Status: pid 2282092 exit 0>]
549
+ #
550
+ # <b>Argument +exe_path+</b>
551
+ #
552
+ # Argument +exe_path+ is one of the following:
553
+ #
554
+ # - The string path to an executable to be called.
555
+ # - A 2-element array containing the path to an executable
556
+ # and the string to be used as the name of the executing process.
557
+ #
558
+ # Example:
559
+ #
560
+ # Open3.capture3('/usr/bin/date')
561
+ # # => ["Thu Sep 28 05:03:51 PM CDT 2023\n", "", #<Process::Status: pid 2282300 exit 0>]
562
+ #
563
+ # Ruby invokes the executable directly, with no shell and no shell expansion:
564
+ #
565
+ # Open3.capture3('doesnt_exist') # Raises Errno::ENOENT
566
+ #
567
+ # If one or more +args+ is given, each is an argument or option
568
+ # to be passed to the executable:
569
+ #
570
+ # Open3.capture3('echo', 'C #')
571
+ # # => ["C #\n", "", #<Process::Status: pid 2282368 exit 0>]
572
+ # Open3.capture3('echo', 'hello', 'world')
573
+ # # => ["hello world\n", "", #<Process::Status: pid 2282372 exit 0>]
278
574
  #
279
575
  def capture3(*cmd)
280
576
  if Hash === cmd.last
@@ -308,34 +604,94 @@ module Open3
308
604
  end
309
605
  module_function :capture3
310
606
 
311
- # Open3.capture2 captures the standard output of a command.
607
+ # :call-seq:
608
+ # Open3.capture2([env, ] command_line, options = {}) -> [stdout_s, status]
609
+ # Open3.capture2([env, ] exe_path, *args, options = {}) -> [stdout_s, status]
610
+ #
611
+ # Basically a wrapper for Open3.popen3 that:
612
+ #
613
+ # - Creates a child process, by calling Open3.popen3 with the given arguments
614
+ # (except for certain entries in hash +options+; see below).
615
+ # - Returns as string +stdout_s+ the standard output of the child process.
616
+ # - Returns as +status+ a <tt>Process::Status</tt> object
617
+ # that represents the exit status of the child process.
618
+ #
619
+ # Returns the array <tt>[stdout_s, status]</tt>:
620
+ #
621
+ # stdout_s, status = Open3.capture2('echo "Foo"')
622
+ # # => ["Foo\n", #<Process::Status: pid 2326047 exit 0>]
623
+ #
624
+ # Like Process.spawn, this method has potential security vulnerabilities
625
+ # if called with untrusted input;
626
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc].
627
+ #
628
+ # Unlike Process.spawn, this method waits for the child process to exit
629
+ # before returning, so the caller need not do so.
630
+ #
631
+ # Argument +options+ is a hash of options for the new process;
632
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
312
633
  #
313
- # stdout_str, status = Open3.capture2([env,] cmd... [, opts])
634
+ # The hash +options+ is passed to method Open3.popen3;
635
+ # two options have local effect in method Open3.capture2:
314
636
  #
315
- # The arguments env, cmd and opts are passed to Open3.popen3 except
316
- # <code>opts[:stdin_data]</code> and <code>opts[:binmode]</code>. See Process.spawn.
637
+ # - If entry <tt>options[:stdin_data]</tt> exists, the entry is removed
638
+ # and its string value is sent to the command's standard input:
317
639
  #
318
- # If <code>opts[:stdin_data]</code> is specified, it is sent to the command's standard input.
640
+ # Open3.capture2('tee', stdin_data: 'Foo')
319
641
  #
320
- # If <code>opts[:binmode]</code> is true, internal pipes are set to binary mode.
642
+ # # => ["Foo", #<Process::Status: pid 2326087 exit 0>]
643
+ #
644
+ # the internal streams are set to binary mode.
645
+ #
646
+ # The single required argument is one of the following:
647
+ #
648
+ # - +command_line+ if it is a string,
649
+ # and if it begins with a shell reserved word or special built-in,
650
+ # or if it contains one or more metacharacters.
651
+ # - +exe_path+ otherwise.
652
+ #
653
+ # <b>Argument +command_line+</b>
654
+ #
655
+ # \String argument +command_line+ is a command line to be passed to a shell;
656
+ # it must begin with a shell reserved word, begin with a special built-in,
657
+ # or contain meta characters:
658
+ #
659
+ # Open3.capture2('if true; then echo "Foo"; fi') # Shell reserved word.
660
+ # # => ["Foo\n", #<Process::Status: pid 2326131 exit 0>]
661
+ # Open3.capture2('echo') # Built-in.
662
+ # # => ["\n", #<Process::Status: pid 2326139 exit 0>]
663
+ # Open3.capture2('date > date.tmp') # Contains meta character.
664
+ # # => ["", #<Process::Status: pid 2326174 exit 0>]
665
+ #
666
+ # The command line may also contain arguments and options for the command:
667
+ #
668
+ # Open3.capture2('echo "Foo"')
669
+ # # => ["Foo\n", #<Process::Status: pid 2326183 exit 0>]
670
+ #
671
+ # <b>Argument +exe_path+</b>
672
+ #
673
+ # Argument +exe_path+ is one of the following:
674
+ #
675
+ # - The string path to an executable to be called.
676
+ # - A 2-element array containing the path to an executable
677
+ # and the string to be used as the name of the executing process.
321
678
  #
322
679
  # Example:
323
680
  #
324
- # # factor is a command for integer factorization.
325
- # o, s = Open3.capture2("factor", :stdin_data=>"42")
326
- # p o #=> "42: 2 3 7\n"
327
- #
328
- # # generate x**2 graph in png using gnuplot.
329
- # gnuplot_commands = <<"End"
330
- # set terminal png
331
- # plot x**2, "-" with lines
332
- # 1 14
333
- # 2 1
334
- # 3 8
335
- # 4 5
336
- # e
337
- # End
338
- # image, s = Open3.capture2("gnuplot", :stdin_data=>gnuplot_commands, :binmode=>true)
681
+ # Open3.capture2('/usr/bin/date')
682
+ # # => ["Fri Sep 29 01:00:39 PM CDT 2023\n", #<Process::Status: pid 2326222 exit 0>]
683
+ #
684
+ # Ruby invokes the executable directly, with no shell and no shell expansion:
685
+ #
686
+ # Open3.capture2('doesnt_exist') # Raises Errno::ENOENT
687
+ #
688
+ # If one or more +args+ is given, each is an argument or option
689
+ # to be passed to the executable:
690
+ #
691
+ # Open3.capture2('echo', 'C #')
692
+ # # => ["C #\n", #<Process::Status: pid 2326267 exit 0>]
693
+ # Open3.capture2('echo', 'hello', 'world')
694
+ # # => ["hello world\n", #<Process::Status: pid 2326299 exit 0>]
339
695
  #
340
696
  def capture2(*cmd)
341
697
  if Hash === cmd.last
@@ -369,21 +725,94 @@ module Open3
369
725
  end
370
726
  module_function :capture2
371
727
 
372
- # Open3.capture2e captures the standard output and the standard error of a command.
728
+ # :call-seq:
729
+ # Open3.capture2e([env, ] command_line, options = {}) -> [stdout_and_stderr_s, status]
730
+ # Open3.capture2e([env, ] exe_path, *args, options = {}) -> [stdout_and_stderr_s, status]
731
+ #
732
+ # Basically a wrapper for Open3.popen3 that:
733
+ #
734
+ # - Creates a child process, by calling Open3.popen3 with the given arguments
735
+ # (except for certain entries in hash +options+; see below).
736
+ # - Returns as string +stdout_and_stderr_s+ the merged standard output
737
+ # and standard error of the child process.
738
+ # - Returns as +status+ a <tt>Process::Status</tt> object
739
+ # that represents the exit status of the child process.
740
+ #
741
+ # Returns the array <tt>[stdout_and_stderr_s, status]</tt>:
742
+ #
743
+ # stdout_and_stderr_s, status = Open3.capture2e('echo "Foo"')
744
+ # # => ["Foo\n", #<Process::Status: pid 2371692 exit 0>]
373
745
  #
374
- # stdout_and_stderr_str, status = Open3.capture2e([env,] cmd... [, opts])
746
+ # Like Process.spawn, this method has potential security vulnerabilities
747
+ # if called with untrusted input;
748
+ # see {Command Injection}[rdoc-ref:command_injection.rdoc].
375
749
  #
376
- # The arguments env, cmd and opts are passed to Open3.popen3 except
377
- # <code>opts[:stdin_data]</code> and <code>opts[:binmode]</code>. See Process.spawn.
750
+ # Unlike Process.spawn, this method waits for the child process to exit
751
+ # before returning, so the caller need not do so.
378
752
  #
379
- # If <code>opts[:stdin_data]</code> is specified, it is sent to the command's standard input.
753
+ # Argument +options+ is a hash of options for the new process;
754
+ # see {Execution Options}[rdoc-ref:Process@Execution+Options].
380
755
  #
381
- # If <code>opts[:binmode]</code> is true, internal pipes are set to binary mode.
756
+ # The hash +options+ is passed to method Open3.popen3;
757
+ # two options have local effect in method Open3.capture2e:
758
+ #
759
+ # - If entry <tt>options[:stdin_data]</tt> exists, the entry is removed
760
+ # and its string value is sent to the command's standard input:
761
+ #
762
+ # Open3.capture2e('tee', stdin_data: 'Foo')
763
+ # # => ["Foo", #<Process::Status: pid 2371732 exit 0>]
764
+ #
765
+ # the internal streams are set to binary mode.
766
+ #
767
+ # The single required argument is one of the following:
768
+ #
769
+ # - +command_line+ if it is a string,
770
+ # and if it begins with a shell reserved word or special built-in,
771
+ # or if it contains one or more metacharacters.
772
+ # - +exe_path+ otherwise.
773
+ #
774
+ # <b>Argument +command_line+</b>
775
+ #
776
+ # \String argument +command_line+ is a command line to be passed to a shell;
777
+ # it must begin with a shell reserved word, begin with a special built-in,
778
+ # or contain meta characters:
779
+ #
780
+ # Open3.capture2e('if true; then echo "Foo"; fi') # Shell reserved word.
781
+ # # => ["Foo\n", #<Process::Status: pid 2371740 exit 0>]
782
+ # Open3.capture2e('echo') # Built-in.
783
+ # # => ["\n", #<Process::Status: pid 2371774 exit 0>]
784
+ # Open3.capture2e('date > date.tmp') # Contains meta character.
785
+ # # => ["", #<Process::Status: pid 2371812 exit 0>]
786
+ #
787
+ # The command line may also contain arguments and options for the command:
788
+ #
789
+ # Open3.capture2e('echo "Foo"')
790
+ # # => ["Foo\n", #<Process::Status: pid 2326183 exit 0>]
791
+ #
792
+ # <b>Argument +exe_path+</b>
793
+ #
794
+ # Argument +exe_path+ is one of the following:
795
+ #
796
+ # - The string path to an executable to be called.
797
+ # - A 2-element array containing the path to an executable
798
+ # and the string to be used as the name of the executing process.
382
799
  #
383
800
  # Example:
384
801
  #
385
- # # capture make log
386
- # make_log, s = Open3.capture2e("make")
802
+ # Open3.capture2e('/usr/bin/date')
803
+ # # => ["Sat Sep 30 09:01:46 AM CDT 2023\n", #<Process::Status: pid 2371820 exit 0>]
804
+ #
805
+ # Ruby invokes the executable directly, with no shell and no shell expansion:
806
+ #
807
+ # Open3.capture2e('doesnt_exist') # Raises Errno::ENOENT
808
+ #
809
+ # If one or more +args+ is given, each is an argument or option
810
+ # to be passed to the executable:
811
+ #
812
+ # Open3.capture2e('echo', 'C #')
813
+ # # => ["C #\n", #<Process::Status: pid 2371856 exit 0>]
814
+ # Open3.capture2e('echo', 'hello', 'world')
815
+ # # => ["hello world\n", #<Process::Status: pid 2371894 exit 0>]
387
816
  #
388
817
  def capture2e(*cmd)
389
818
  if Hash === cmd.last
@@ -758,3 +1187,6 @@ module Open3
758
1187
  end
759
1188
 
760
1189
  end
1190
+
1191
+ # JRuby uses different popen logic on Windows, require it here to reuse wrapper methods above.
1192
+ require 'open3/jruby_windows' if RUBY_ENGINE == 'jruby' && JRuby::Util::ON_WINDOWS
data/open3.gemspec CHANGED
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  name = File.basename(__FILE__, ".gemspec")
4
- version = ["lib", Array.new(name.count("-"), "..").join("/")].find do |dir|
5
- break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
4
+ version = ["lib", Array.new(name.count("-")+1, "..").join("/")].find do |dir|
5
+ break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}/version.rb")) do |line|
6
6
  /^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
7
7
  end rescue nil
8
8
  end
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
  # Specify which files should be added to the gem when it is released.
26
26
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
27
27
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
28
- `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
28
+ `git ls-files -z 2>#{IO::NULL}`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
29
29
  end
30
30
  spec.bindir = "exe"
31
31
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: open3
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yukihiro Matsumoto
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-22 00:00:00.000000000 Z
11
+ date: 2023-11-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Popen, but with stderr, too
14
14
  email:
@@ -17,6 +17,8 @@ executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
+ - ".github/dependabot.yml"
21
+ - ".github/workflows/test-jruby.yml"
20
22
  - ".github/workflows/test.yml"
21
23
  - ".gitignore"
22
24
  - Gemfile
@@ -26,6 +28,8 @@ files:
26
28
  - bin/console
27
29
  - bin/setup
28
30
  - lib/open3.rb
31
+ - lib/open3/jruby_windows.rb
32
+ - lib/open3/version.rb
29
33
  - open3.gemspec
30
34
  homepage: https://github.com/ruby/open3
31
35
  licenses:
@@ -49,7 +53,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
53
  - !ruby/object:Gem::Version
50
54
  version: '0'
51
55
  requirements: []
52
- rubygems_version: 3.2.2
56
+ rubygems_version: 3.5.0.dev
53
57
  signing_key:
54
58
  specification_version: 4
55
59
  summary: Popen, but with stderr, too