Maintenance Ruby 3.4 gets fixes, but no new features. Switch to Ruby 4.0 →
Open3.popen3
# File tmp/rubies/ruby-3.4.1/lib/open3.rb, line 218
def popen3(*cmd, &block)
if Hash === cmd.last
opts = cmd.pop.dup
else
opts = {}
end
in_r, in_w = IO.pipe
opts[:in] = in_r
in_w.sync = true
out_r, out_w = IO.pipe
opts[:out] = out_w
err_r, err_w = IO.pipe
opts[:err] = err_w
popen_run(cmd, opts, [in_r, out_w, err_w], [in_w, out_r, err_r], &block)
end
Basically a wrapper for Process.spawn that:
-
Creates a child process, by calling
Process.spawnwith the given arguments. -
Creates streams
stdin,stdout, andstderr, which are the standard input, standard output, and standard error streams in the child process. -
Creates thread
wait_threadthat waits for the child process to exit; the thread has methodpid, which returns the process ID of the child process.
With no block given, returns the array [stdin, stdout, stderr, wait_thread]. The caller should close each of the three returned streams.
stdin, stdout, stderr, wait_thread = Open3.popen3('echo') # => [#<IO:fd 8>, #<IO:fd 10>, #<IO:fd 12>, #<Process::Waiter:0x00007f58d5428f58 run>] stdin.close stdout.close stderr.close wait_thread.pid # => 2210481 wait_thread.value # => #<Process::Status: pid 2210481 exit 0>
With a block given, calls the block with the four variables (three streams and the wait thread) and returns the block’s return value. The caller need not close the streams:
Open3.popen3('echo') do |stdin, stdout, stderr, wait_thread| p stdin p stdout p stderr p wait_thread p wait_thread.pid p wait_thread.value end
Output:
#<IO:fd 6> #<IO:fd 7> #<IO:fd 9> #<Process::Waiter:0x00007f58d53606e8 sleep> 2211047 #<Process::Status: pid 2211047 exit 0>
Like Process.spawn, this method has potential security vulnerabilities if called with untrusted input; see Command Injection.
Unlike Process.spawn, this method waits for the child process to exit before returning, so the caller need not do so.
If the first argument is a hash, it becomes leading argument env in the call to Process.spawn; see Execution Environment.
If the last argument is a hash, it becomes trailing argument options in the call to Process.spawn; see Execution Options.
The single required argument is one of the following:
-
command_lineif it is a string, and if it begins with a shell reserved word or special built-in, or if it contains one or more metacharacters. -
exe_pathotherwise.
Argument command_line
String argument command_line is a command line to be passed to a shell; it must begin with a shell reserved word, begin with a special built-in, or contain meta characters:
Open3.popen3('if true; then echo "Foo"; fi') {|*args| p args } # Shell reserved word. Open3.popen3('echo') {|*args| p args } # Built-in. Open3.popen3('date > date.tmp') {|*args| p args } # Contains meta character.
Output (similar for each call above):
[#<IO:(closed)>, #<IO:(closed)>, #<IO:(closed)>, #<Process::Waiter:0x00007f58d52f28c8 dead>]
The command line may also contain arguments and options for the command:
Open3.popen3('echo "Foo"') { |i, o, e, t| o.gets } "Foo\n"
Argument exe_path
Argument exe_path is one of the following:
-
The string path to an executable to be called.
-
A 2-element array containing the path to an executable and the string to be used as the name of the executing process.
Example:
Open3.popen3('/usr/bin/date') { |i, o, e, t| o.gets } # => "Wed Sep 27 02:56:44 PM CDT 2023\n"
Ruby invokes the executable directly, with no shell and no shell expansion:
Open3.popen3('doesnt_exist') { |i, o, e, t| o.gets } # Raises Errno::ENOENT
If one or more args is given, each is an argument or option to be passed to the executable:
Open3.popen3('echo', 'C #') { |i, o, e, t| o.gets } # => "C #\n" Open3.popen3('echo', 'hello', 'world') { |i, o, e, t| o.gets } # => "hello world\n"
Take care to avoid deadlocks. Output streams stdout and stderr have fixed-size buffers, so reading extensively from one but not the other can cause a deadlock when the unread buffer fills. To avoid that, stdout and stderr should be read simultaneously (using threads or IO.select).
Related:
-
Open3.popen2: Makes the standard input and standard output streams of the child process available as separate streams, with no access to the standard error stream. -
Open3.popen2e: Makes the standard input and the merge of the standard output and standard error streams of the child process available as separate streams.