In Progress
Unit 1, Lesson 21
In Progress

Backticks

Video transcript & code

I'm going to admit right up front that today's episode is more of a stupid Ruby trick than a useful technique. Sometimes it's fun to poke around in Ruby's dusty attic and see what oddities come to light. At least, it's fun for me; you may or may not agree.

You probably know that one way to execute a subprocess in Ruby is to use a backtick-quoted string, like this:

sysinfo = `uname -a`
sysinfo # => "Linux petronius 3.2.0-32-generic #51-Ubuntu SMP Wed Sep 26 21:33:09 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux\n"

There's also a percent-quoting form, %x. With this form you can choose any delimiters you want instead of backticks. This is useful when the command itself contains backticks.

output = %x{finger `whoami`}
puts output
# >> Login: avdi                                Name: Avdi Grimm
# >> Directory: /home/avdi                      Shell: /bin/zsh
# >> On since Sun Nov  4 23:42 (EST) on tty7    17 hours 45 minutes idle
# >> On since Sun Nov  4 23:43 (EST) on pts/0 from :0
# >>    40 seconds idle
# >>      (messages off)
# >> On since Mon Nov  5 14:43 (EST) on pts/1 from :0
# >>    2 hours 11 minutes idle
# >>      (messages off)
# >> New mail received Mon Oct 15 12:54 2012 (EDT)
# >>      Unread since Wed Nov  2 19:20 2011 (EDT)
# >> No Plan.  

Now for the stupid Ruby trick part. Unlike other quote characters in Ruby, the backticks are actually considered an operator. The backtick operator. And like other operators, this operator can be overloaded or overridden.

Here's an example of overriding the backticks to log all commands that are executed.

alias old_backtick `
def `(command)
  puts "Executing command: #{command}"
  old_backtick(command)
end

`uname -a` # => "Linux petronius 3.2.0-32-generic #51-Ubuntu SMP Wed Sep 26 21:33:09 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux\n"
# >> Executing command: uname -a

This changes how the %x version works as well.

%x{uname -a} # => "Linux petronius 3.2.0-32-generic #51-Ubuntu SMP Wed Sep 26 21:33:09 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux\n"
# >> Executing command: uname -a

Or, we could make the backticks do something completely different in the context of a specific class. Here's a class where the backticks convert a string into a URI. This makes it more convenient to use Net::HTTP methods which expect a URI object, not a raw string.

require 'net/http'
require 'cgi'

class Cowsays
  def `(url)
    URI.parse(url)
  end

  def say(message)
    message = CGI.escape(message)
    Net::HTTP.get_print(`http://www.cowsays.com/cowsay?message=#{message}`)
  end
end

Cowsays.new.say "Hello, RubyTapas!"
# >>  ___________________
# >> < Hello, RubyTapas! >
# >>  -------------------
# >>         \   ^__^
# >>          \  (oo)\_______
# >>             (__)\       )\/\
# >>                 ||----w |
# >>                 ||     ||

Now, is this a good idea? Probably not. Whatever convenience might be gained by having a quoting character that we can overload to do whatever we want, it is offset by how surprising the resulting code is to anyone else who reads it. Ruby coders have an expectation that a backtick is always used to execute a command and return the captured output. I could see where the first example, where we logged every command execution, might have some marginal utility. But anything beyond that is probably more confusing than it's worth.

In the next episode we'll return to topics of more practical use. Until then, happy hacking!

Responses