From: Tom Felker Date: 2003-09-01T10:26:53+09:00 Subject: Re: `echo %!(*` On Mon, 01 Sep 2003 00:21:11 +0000, Mark J. Reed wrote: > On Sun, Aug 31, 2003 at 06:00:34PM -0500, Shashank Date wrote: >> I am not sure that I understand what you mean by "bypassing the shell", > > It's simple. When you execute `some command` (or the equivalent > using %x), what Ruby actually executes is your command interpreter > ("shell" in Unix-speak), passing it the string between `...` > to execute. It's up to the shell to split up the command into > words, handle wildcard expansion (at least on UNIX; the command > interpreter on Windows leaves that latter duty up to the individual > commands), etc. The problem is that all this power is a security > hole because it's easy to trick command interpreters into doing > ugly things just by passing something nefarious as a "filename". > > The original poster mentioned Kernel.system, which is the way to > execute a command when you don't care about its output. > If you pass it one long string, Kernel.system will also invoke the > shell, but if instead you pass it separate arguments for each word of > the command line, it bypasses the shell and executes the command > directly. Thus: > > system("ps -fp#{$$}") # also invokes the shell > system('ps', "-fp#{$$}") # doesn't invoke the shell > psOutput = `ps -fp#{$$}` # invokes the shell > > The question is: how do you complete the list, that is, capture the > command output without involving the shell? > > The only solution of which I'm aware is to use popen/exec: > > if fd = IO.popen('-') then > psOutput = fd.readlines.join("\n") > else > exec 'ps', "-fp#{$$}" > end > > Of course, you could turn this into a method: > > def safeBackticks(*args) > if fd = IO.popen('-') then > output = fd.readlines.join("\n") fd.close #right? > else > exec *args > end > return output > end Thanks, that method is exactly what I'm looking for. I only wonder why it isn't in Ruby already. Although I personally couldn't care less, the above can only be done in Windows by calling CreateProcess() directly. Using quotes only works if the filename doesn't contain quotes. Escaping it would work, but it's a hack, and dependent on the shell. (Windows's cmd will expand %VAR%, IIRC.) It's also kind of weird to have a method whose only name is "`". ri doesn't say what popen("-") does with no block, though I see it returns twice like fork(). Sweet. Have fun, -- Tom Felker, - Stop fiddling with the volume knob. The ability to monopolize a market is insignificant next to the power of the source.