Chaining external commands using pipes.

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
 
K

Konrad Meyer

--nextPart2082345.jyFlp0tXVb
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

I have come up with a little script that works well in IRB for issuing=20
shell commands and piping them together. For example
=20
require './shell.rb'
=20
io =3D Sh.ls("/bin").grep("grep")
puts io
=20
generates
=20
=09
bzegrep
bzfgrep
bzgrep
egrep
fgrep
grep
zegrep
zfgrep
zgrep
=20
I am curious as to whether the way I have implemented it is the best=20
way. It seems kind of clunky to me. I have to spawn a new Ruby process=20
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
=20
The source code is below or syntax highlighted here
http://xtargets.com/snippets/posts/show/77
=20
Brad
=20
--------------------------------
=20
module Sh
class Command
=20
def initialize(cmd, *args)
@cmd =3D cmd
@args =3D args
@io =3D nil
end
=20
def inspect
to_s
end
=20
def to_str
to_s
end
=20
def to_s
call
str =3D @io.read
@io.close
str
end
=20
def | other
call
other.call(@io)
end
=20
def method_missing(name, *args)
self | Command.new(name, *args)
end
=20
def call (io=3Dnil)
=20
# The @io object can only be assigned
# once
return self if @io
=20
command =3D @cmd.to_s + " " + @args.join(" ")
=20
if io
# We need a new process
# and a pipe to connect the
# this command to the previous
# command in the chain.
rd, wr =3D IO.pipe
=20
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 =3D 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 =3D rd
else
# This command is executed standalone.
@io =3D IO.popen command
end
self
end
end
=20
def self.method_missing (name, *args)
Command.new(name, *args)
end
end

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

=2D-=20
Konrad Meyer <[email protected]> http://konrad.sobertillnoon.com/

--nextPart2082345.jyFlp0tXVb
Content-Type: application/pgp-signature; name=signature.asc
Content-Description: This is a digitally signed message part.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)

iD8DBQBG4PUkCHB0oCiR2cwRAqxLAJ4vPkcPTy+CC1aEh68MQszgOh3d2QCdEh6x
6QJV6Otl20G4EHZlFUCQOhU=
=wE3f
-----END PGP SIGNATURE-----

--nextPart2082345.jyFlp0tXVb--
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,968
Messages
2,570,149
Members
46,695
Latest member
StanleyDri

Latest Threads

Top