Skip to content

Capture subprocess output using a non‐standard file descriptor

James Couball edited this page Mar 8, 2025 · 1 revision

This script spawns a subprocess, redirecting the output from file descriptor 3 in the child process the file "output.txt". The parent passes the redirected file descriptor to the child process in the environment variable CUSTOM_FD.

The child process opens the file descriptor and writes the string "Child process writing to file descriptor 3... DONE\n" using the file descriptor.

The parent process checks the file "output.txt" to make it contains the output from the child process.

require 'fileutils'
require 'pp'

def ruby_command(code)
  @ruby_path ||=
    if Gem.win_platform?
      `where ruby`.chomp
    else
      `which ruby`.chomp
    end

  [@ruby_path, '-e', code]
end

# This is the spawned script:
child_script = <<~SCRIPT
  custom_fd = ENV["CUSTOM_FD"].to_i
  custom_io = IO.new(custom_fd)
  custom_io.print "Child process writing to file descriptor \#{custom_fd}..."
  custom_io.puts "DONE"
  custom_io.close
SCRIPT

command = ruby_command(child_script)

child_file_descriptor = 3
capture_filepath = 'output.txt'
status = nil

File.open(capture_filepath, 'w') do |capture_file|
  _pid, status = Process.wait2(
    Process.spawn(
      { 'CUSTOM_FD' => child_file_descriptor.to_s },
      *command,
      child_file_descriptor => capture_file.fileno
    )
  )
end

puts <<~RESULT
  Status: #{status}
  Expected contexts of #{capture_filepath}: "Child process writing to file descriptor 3...DONE\\n"
  Actual context of #{capture_filepath}:    #{File.read(capture_filepath).pretty_inspect}
RESULT
Clone this wiki locally