Install open4 via gem
masterTo use open4 in your Ruby project, install the gem using the following command:
gem install open4~> gem install open4repository·master·Indexed 41 days ago
https://github.com/ahoward/open4A Ruby library for simplifying child process management. It provides tools for spawning processes with access to PIDs and I/O handles (stdin, stdout, stderr) via methods like popen4, pfork4, and spawn. Features include background process execution with the bg method, fine-grained file descriptor control with popen4ext, process termination via maim, and built-in timeout and exit status validation.
To use open4 in your Ruby project, install the gem using the following command:
gem install open4~> gem install open4When using spawn, you can control how the library reacts to the child process's exit status using the following options:
:status => true: Returns the Process::Status object without raising an error on non-zero exits.:status => integer: Raises an error if the exit status is not equal to the provided integer.:status => [int1, int2, ...]: Raises an error if the exit status is not in the provided list.:exitstatus => [int1, int2, ...]: (Older versions) Similar to :status with an array of acceptable statuses.include Open4
# Raises error if exit status is not 0 or 42
spawn 'ruby -e "exit 42"', :exitstatus => [0, 42]
# Returns status object instead of raising error
status = spawn 'ruby -e "exit 42"', :status => trueOpen4 supports several timeout configurations to ensure processes do not run indefinitely or hang on I/O:
:timeout: An overall timeout for the command execution.:stdout_timeout: A timeout specifically for the stdout stream.:stdin_timeout: A timeout specifically for the stdin stream.# Overall timeout
open4.spawn cmd, :timeout => 60
# Individual I/O timeouts
open4.spawn cmd, :stdout_timeout => 2
open4.spawn cmd, :stdin_timeout => 1When calling Open4::popen4 with a block, the child process is automatically waited for once the block finishes. The method returns the Process::Status object of the child process.
require 'open4'
status = Open4::popen4("sh") do |pid, stdin, stdout, stderr|
stdin.puts "echo 42.out"
stdin.puts "echo 42.err 1>&2"
stdin.close
puts "pid : #{ pid }"
puts "stdout : #{ stdout.read.strip }"
puts "stderr : #{ stderr.read.strip }"
end
puts "status : #{ status.inspect }"
puts "exitstatus : #{ status.exitstatus }"Use alive?(pid) (or the alias alive) to check if a process with the given PID is currently running. It returns true if the process exists and false otherwise.
if Open4.alive?(1234)
puts "Process is running"
endThe bg (background) method runs a process in a separate thread. The returned thread object has dynamic methods added to it, including .pid and .exitstatus (which is a blocking call).
require 'yaml'
require 'open4'
include Open4
stdin = '42'
stdout = ''
stderr = ''
# Returns a thread
t = bg 'ruby -e"sleep 4; puts ARGF.read"', 0=>stdin, 1=>stdout, 2=>stderr
# t.exitstatus is a blocking call
waiter = Thread.new{ y t.pid => t.exitstatus }
while((status = t.status))
y "status" => status
sleep 1
end
waiter.join
y "stdout" => stdoutThe background method (aliased as bg) spawns a process in a separate thread so the parent can continue execution immediately. It returns a special thread object that allows you to query the process status.
Returned Object Methods:
pid: Returns the PID of the spawned process.spawn_status: Returns the Process::Status object.exitstatus: Returns the exit status of the process.proc = Open4.background('sleep', '10')
puts "Started process with PID: #{proc.pid}"
# ... later
puts "Exit status: #{proc.exitstatus}"The bg (background) method is similar to spawn, but it automatically runs the process in a new Ruby thread. The returned thread object has dynamic methods added to it, including .pid and .exitstatus (which is a blocking call). You can also check the current state of the process using .status.
require 'yaml'
require 'open4'
include Open4
stdin = '42'
stdout = ''
stderr = ''
# Returns a thread
t = bg 'ruby -e"sleep 4; puts ARGF.read"', 0=>stdin, 1=>stdout, 2=>stderr
# t.exitstatus is a blocking call
waiter = Thread.new{ y t.pid => t.exitstatus }
while((status = t.status))
y "status" => status
sleep 1
end
waiter.join
p stdoutThe Open4::popen4 method opens a child process and provides handles for the process ID (pid), standard input (stdin), standard output (stdout), and standard error (stderr).
In its basic form, you are responsible for waiting for the process and reading the IO handles.
require "open4"
pid, stdin, stdout, stderr = Open4::popen4 "sh"
stdin.puts "echo 42.out"
stdin.puts "echo 42.err 1>&2"
stdin.close
# You must manually wait for the process
ignored, status = Process::waitpid2 pid
puts "pid : #{ pid }"
puts "stdout : #{ stdout.read.strip }"
puts "stderr : #{ stderr.read.strip }"
puts "status : #{ status.inspect }"
puts "exitstatus : #{ status.exitstatus }"require "open4"
pid, stdin, stdout, stderr = Open4::popen4 "sh"
stdin.puts "echo 42.out"
stdin.puts "echo 42.err 1>&2"
stdin.close
ignored, status = Process::waitpid2 pid
puts "pid : #{ pid }"
puts "stdout : #{ stdout.read.strip }"
puts "stderr : #{ stderr.read.strip }"
puts "status : #{ status.inspect }"
puts "exitstatus : #{ status.exitstatus }"Use popen4ext if you need fine-grained control over file descriptor closing. It behaves similarly to popen4 but includes a closefds boolean parameter. If true, it attempts to close all file descriptors in the child process except for the standard ones (0, 1, 2) and the pipes used for communication.
Open4.popen4ext(true, 'my_command', 'arg1') do |pid, stdin, stdout, stderr|
# ...
endThe pfork4 method is similar to popen4, but instead of executing an external command, it runs a Ruby lambda or proc in a child process. If the child process raises an exception, it is marshaled and propagated to the parent process.
require 'open4'
echo = lambda do
$stdout.write $stdin.read
raise 'finish implementing me'
end
org_message = "hello, world!"
got_message = nil
exception = nil
begin
Open4.pfork4(echo) do |cid, stdin, stdout, stderr|
stdin.write org_message
stdin.close
got_message = stdout.read
end
rescue RuntimeError => e
exception = e.to_s
end
puts "org_message: #{org_message}"
puts "got_message: #{got_message}"
puts "exception : #{exception}"The pfork4 method forks a child process to execute a block of code. It is designed to make Kernel#exit calls within the block behave as if they were normal process terminations for the child, rather than exiting the parent.
Behavior:
exit, the child process terminates normally.exit!, it bypasses the rescue logic.Open4.pfork4 do
puts "I am in a child process"
exit 0
end