B
Brad Phelan
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
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