From: evanm@... Date: 2004-06-21T06:06:02+09:00 Subject: Cleaning up IO#popen 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::pipe # pipe[0] for read, pipe[1] for write pr = IO::pipe pe = IO::pipe 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::pipe begin if pid=fork yield(rd,pid) else yield(wr,pid) exit! end Process.waitpid(pid) ensure rd.close wr.close end end end