E
evanm
I think IO#popen could stand to be changed, and I wanted to run my ideas by
this list. Currently the problems with popen are:
* can't bypass the shell
* takes a "mode string" arg for what really should be a separate function
* takes a "-" as a magic value for what really should be a separate function
* generally kludgy
While looking around for ways to improve on it I came across the Open3
module in the stdlib, which has a much more "ruby" interface. Based mostly on
that, I redid some methods that could replace IO.popen (and put popen3 in IO
at the same time, which is where it probably belongs)
The only problem I've identified with what I have below is that I close
stderr, and this may be wrong. I also have a version that doesn't close
stderr, but I don't know which is the Right Way (Perl's IPC::Open2 doesn't
close stderr).
Can I get some opinions? There's quite a few core ruby methods that seem to
exist simply because there's some C function by that name, and that leaves
the language with a definate non-ruby feel. This is intended to replace the
current popen, and make all the popen calls consistant in operation. There's
also IO.fork_open, which is my rendition of IO.popen('-').
Thanks in advance for your input!
# Shamelessly copied from the Open3 module
def IO.popen3(cmd)
pw = IO:ipe # pipe[0] for read, pipe[1] for write
pr = IO:ipe
pe = IO:ipe
pid = fork{
# child
fork{
# grandchild
pw[1].close
STDIN.reopen(pw[0])
pw[0].close
pr[0].close
STDOUT.reopen(pr[1])
pr[1].close
pe[0].close
STDERR.reopen(pe[1])
pe[1].close
exec(*cmd)
}
exit!
}
pw[0].close
pr[1].close
pe[1].close
Process.waitpid(pid)
pi = [pw[1], pr[0], pe[0]]
pw[1].sync = true
if block_given?
begin
return yield(*pi)
ensure
pi.each{|p| p.close unless p.closed?}
end
end
pi
end
def IO.popen2(cmd, &block)
ret = IO.popen3(cmd, &block)
if block_given?
return ret
else
ret[2].close
return [ret[0], ret[1]]
end
end
def IO.popen(cmd, &block)
ret = IO.popen3(cmd, &block)
if block_given?
return ret
else
ret[2].close
ret[0].close
return ret[1]
end
end
def IO.fork_open
if block_given?
rd,wr = IO:ipe
begin
if pid=fork
yield(rd,pid)
else
yield(wr,pid)
exit!
end
Process.waitpid(pid)
ensure
rd.close
wr.close
end
end
end
this list. Currently the problems with popen are:
* can't bypass the shell
* takes a "mode string" arg for what really should be a separate function
* takes a "-" as a magic value for what really should be a separate function
* generally kludgy
While looking around for ways to improve on it I came across the Open3
module in the stdlib, which has a much more "ruby" interface. Based mostly on
that, I redid some methods that could replace IO.popen (and put popen3 in IO
at the same time, which is where it probably belongs)
The only problem I've identified with what I have below is that I close
stderr, and this may be wrong. I also have a version that doesn't close
stderr, but I don't know which is the Right Way (Perl's IPC::Open2 doesn't
close stderr).
Can I get some opinions? There's quite a few core ruby methods that seem to
exist simply because there's some C function by that name, and that leaves
the language with a definate non-ruby feel. This is intended to replace the
current popen, and make all the popen calls consistant in operation. There's
also IO.fork_open, which is my rendition of IO.popen('-').
Thanks in advance for your input!
# Shamelessly copied from the Open3 module
def IO.popen3(cmd)
pw = IO:ipe # pipe[0] for read, pipe[1] for write
pr = IO:ipe
pe = IO:ipe
pid = fork{
# child
fork{
# grandchild
pw[1].close
STDIN.reopen(pw[0])
pw[0].close
pr[0].close
STDOUT.reopen(pr[1])
pr[1].close
pe[0].close
STDERR.reopen(pe[1])
pe[1].close
exec(*cmd)
}
exit!
}
pw[0].close
pr[1].close
pe[1].close
Process.waitpid(pid)
pi = [pw[1], pr[0], pe[0]]
pw[1].sync = true
if block_given?
begin
return yield(*pi)
ensure
pi.each{|p| p.close unless p.closed?}
end
end
pi
end
def IO.popen2(cmd, &block)
ret = IO.popen3(cmd, &block)
if block_given?
return ret
else
ret[2].close
return [ret[0], ret[1]]
end
end
def IO.popen(cmd, &block)
ret = IO.popen3(cmd, &block)
if block_given?
return ret
else
ret[2].close
ret[0].close
return ret[1]
end
end
def IO.fork_open
if block_given?
rd,wr = IO:ipe
begin
if pid=fork
yield(rd,pid)
else
yield(wr,pid)
exit!
end
Process.waitpid(pid)
ensure
rd.close
wr.close
end
end
end