Chaining external commands using pipes

I have come up with a little script that works well in IRB for issuing
shell commands and piping them together. For example

require ‘./shell.rb’

io = Sh.ls(“/bin”).grep(“grep”)
puts io

generates

bzegrep
bzfgrep
bzgrep
egrep
fgrep
grep
zegrep
zfgrep
zgrep

I am curious as to whether the way I have implemented it is the best
way. It seems kind of clunky to me. I have to spawn a new Ruby process
using fork and then reopen $stdin on the child process and connect it
to the output stream of the previous command. I was hoping to find
a way to get around forking ruby processes to do this. Any
ideas

The source code is below or syntax highlighted here
http://xtargets.com/snippets/posts/show/77

Brad


module Sh
class Command

     def initialize(cmd, *args)
         @cmd = cmd
         @args = args
         @io = nil
     end

     def inspect
         to_s
     end

     def to_str
         to_s
     end

     def to_s
         call
         str = @io.read
         @io.close
         str
     end

     def | other
         call
         other.call(@io)
     end

     def method_missing(name, *args)
         self | Command.new(name, *args)
     end

     def call (io=nil)

         # The @io object can only be assigned
         # once
         return self if @io

         command =  @cmd.to_s + " " + @args.join(" ")

         if io
             #  We need a new process
             #  and a pipe to connect the
             #  this command to the previous
             #  command in the chain.
             rd, wr = IO.pipe

             if fork
                 # Parent
                 wr.close
             else
                 # Child
                 rd.close
                 begin
                     # Reconnect the STDIN of the
                     # new process to the io parameter
                     $stdin.reopen(io)
                     IO.popen command do |f|
                         while txt = f.read(4096)
                             wr.write txt
                         end
                     end
                 rescue Errno::EPIPE
                     # Ignore the broken pipe
                     # cause the other process
                     # has thrown away the pipe
                     # earlier than we used outs
                 ensure
                     wr.close
                 end
                 exit
             end
             @io = rd
         else
             # This command is executed standalone.
             @io = IO.popen command
         end
         self
     end
 end

 def self.method_missing (name, *args)
     Command.new(name, *args)
 end

end

On Thursday 06 September 2007 11:35:05 pm Brad P. wrote:

I am curious as to whether the way I have implemented it is the best

         str = @io.read
         self | Command.new(name, *args)
         if io
                 # Child
                 rescue Errno::EPIPE
         else

end
Stdlib shell (require ‘shell’) does much of that IIRC.
( http://ruby-doc.org/stdlib/libdoc/shell/rdoc/classes/Shell.html )

Stdlib shell (require ‘shell’) does much of that IIRC.
( http://ruby-doc.org/stdlib/libdoc/shell/rdoc/classes/Shell.html )

Perhaps but there is zero documentation for any of the classes in the
shell library and I don’t see anything specific to creating pipes
between processes there.

Brad