Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Created December 10, 2016 22:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoshCheek/61769bfa05d52609e15948fabfad3381 to your computer and use it in GitHub Desktop.
Save JoshCheek/61769bfa05d52609e15948fabfad3381 to your computer and use it in GitHub Desktop.
A shell pipeline in Ruby
# Read initial standard input from the data segment at the end of the file
# You can also see what is ultimately output down there
pos = DATA.pos # => 1617
$stdin.reopen __FILE__ # => #<IO:/var/folders/7g/mbft22555w3_2nqs_h1kbglw0000gn/T/seeing_is_believing_temp_dir20161210-36802-czciw7/program.rb>
$stdin.seek pos, :SET # => 0
# The commands that will be piped together
commands = [
%w[cat],
%w[tr a A],
%w[tr c C],
]
# Connnect each command's to our stderr, first has our stdin, last has our stdout
pipeline = commands.map do |command|
{ command: command,
stdin: $stdin,
stdout: $stdout,
stderr: $stderr,
close_stdin: false,
close_stdout: false,
}
end
# Pipe each command's stdout to the next command's stdin
pipeline.each_cons(2) do |(left, right)|
right[:stdin], left[:stdout] = IO.pipe
right[:close_stdin] = true
left[:close_stdout] = true
end
# Execute each command
pipeline.each do |attrs|
# if we are the parent process
if pid = fork
# Document the pid, close the piped file descriptors, on to the next
attrs[:pid] = pid
attrs[:stdin].close if attrs[:close_stdin]
attrs[:stdout].close if attrs[:close_stdout]
next
end
# Subprocess sets the file descriptors and execs the command
STDIN.reopen attrs[:stdin]
STDOUT.reopen attrs[:stdout]
STDERR.reopen attrs[:stderr]
exec *attrs[:command]
end
# Wait for the children to finish, record their exit statuses
pipeline.each do |attrs|
Process.wait attrs[:pid]
attrs[:status] = $?
end
pipeline
# => [{:command=>["cat"],
# :stdin=>
# #<IO:/var/folders/7g/mbft22555w3_2nqs_h1kbglw0000gn/T/seeing_is_believing_temp_dir20161210-36802-czciw7/program.rb>,
# :stdout=>#<IO:(closed)>,
# :stderr=>#<IO:<STDERR>>,
# :close_stdin=>false,
# :close_stdout=>true,
# :pid=>36804,
# :status=>#<Process::Status: pid 36804 exit 0>},
# {:command=>["tr", "a", "A"],
# :stdin=>#<IO:(closed)>,
# :stdout=>#<IO:(closed)>,
# :stderr=>#<IO:<STDERR>>,
# :close_stdin=>true,
# :close_stdout=>true,
# :pid=>36805,
# :status=>#<Process::Status: pid 36805 exit 0>},
# {:command=>["tr", "c", "C"],
# :stdin=>#<IO:(closed)>,
# :stdout=>#<IO:<STDOUT>>,
# :stderr=>#<IO:<STDERR>>,
# :close_stdin=>true,
# :close_stdout=>false,
# :pid=>36806,
# :status=>#<Process::Status: pid 36806 exit 0>}]
# >> AAA
# >> bbb
# >> CCC
__END__
aaa
bbb
ccc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment