Nov 3 2007

Backticks 2.0

When you’ve been bitten by spaces and other odd characters in filenames as
often as I have, you begin to get not a little bit paranoid. Beginners often
make this mistake (ruby syntax, but the same thing happens in bash, perl,
python, etc.):

foo = `file #{filename}`

They quickly learn to do this instead:

foo = `file "#{filename}"`

This works fine until they come across a filename with quotes or any other
characters that are special to the shell from within quotes.

What’s needed is something akin to what can be done with exec and system.
With those two, you can do something like this and it doesn’t matter what
crazy filename is thrown at it, you won’t have any trouble:

system 'file', filename

But exec and system don’t return stdout, like backticks do. The former
doesn’t return at all, and the latter returns the return value. Here is an
idiom that works like glorified array-parameter backticks in ruby:

def backtick(cmd,*args)
  IO.popen('-') {|f| f ? f.read : exec(cmd,*args)}
end

Everything you need to understand that code can be found in ri IO.popen and
ri Kernel.exec. If you can think of a better name than backtick do let me
know. Now our code becomes paranoid-friendly:

foo = backtick('file',filename)

While we’re on the subject, a very handy method is Open3#popen3, which is a
bit overkill for this glorified backticks problem but could very well simplify
your life (or mine) in the future.