End of Life Ruby 3.2 has reached end of life and gets no more fixes. Switch to Ruby 4.0 →
Open3#popen3
# File tmp/rubies/ruby-3.2.0/lib/open3.rb, line 86
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
Open stdin, stdout, and stderr streams and start external executable. In addition, a thread to wait for the started process is created. The thread has a pid method and a thread variable :pid which is the pid of the started process.
Block form:
Open3.popen3([env,] cmd... [, opts]) {|stdin, stdout, stderr, wait_thr|
pid = wait_thr.pid # pid of the started process.
...
exit_status = wait_thr.value # Process::Status object returned.
}
Non-block form:
stdin, stdout, stderr, wait_thr = Open3.popen3([env,] cmd... [, opts]) pid = wait_thr[:pid] # pid of the started process ... stdin.close # stdin, stdout and stderr should be closed explicitly in this form. stdout.close stderr.close exit_status = wait_thr.value # Process::Status object returned.
The parameters env, cmd, and opts are passed to Process.spawn. A commandline string and a list of argument strings can be accepted as follows:
Open3.popen3("echo abc") {|i, o, e, t| ... }
Open3.popen3("echo", "abc") {|i, o, e, t| ... }
Open3.popen3(["echo", "argv0"], "abc") {|i, o, e, t| ... }
If the last parameter, opts, is a Hash, it is recognized as an option for Process.spawn.
Open3.popen3("pwd", :chdir=>"/") {|i,o,e,t| p o.read.chomp #=> "/" }
wait_thr.value waits for the termination of the process. The block form also waits for the process when it returns.
Closing stdin, stdout and stderr does not wait for the process to complete.
You should be careful to avoid deadlocks. Since pipes are fixed length buffers, Open3.popen3(“prog”) {|i, o, e, t| o.read } deadlocks if the program generates too much output on stderr. You should read stdout and stderr simultaneously (using threads or IO.select). However, if you don’t need stderr output, you can use Open3.popen2. If merged stdout and stderr output is not a problem, you can use Open3.popen2e. If you really need stdout and stderr output as separate strings, you can consider Open3.capture3.