Programmers often talk about how easy it is to interact with the shell from Ruby, using the %x[] or backtick syntax to fire off work.

These conveniences are great for most situations, but they start to fall down when presented with unusual inputs or strange pipeline gymnastics.

Spaces in Filenames

For example, a script to echo out the contents of a file might look like this.

#!/usr/bin/rubyputs`cat #{ARGV[0]}`

This seems okay at first glance, if a little nonidiomatic. Upon closer inspection, however, we can see how it would fail when presented with a file whose name includes a space.

The simplest solution to this problem is to use a command-execution method that allows us to pass explicit arguments.

#!/usr/bin/rubyputsexec("cat", ARGV[0])

By not conflating the command to be executed and its arguments, this code avoids the issue we saw with unusual filenames. Unfortunately, it has lost the ability to effectively harness the power of the Unix pipeline.

Supporting Pipes

Let’s reformulate that first example and add a pipe to the mix.

#!/usr/bin/rubyputs`cat #{ARGV[0]} | sort`

This code works fine as long as the file we’re working with has no spaces in its filename. As of Ruby 1.9, it’s much simpler to deal with this scenario in a robust way. We can use Open3::pipeline_r to explicitly chain together several processes.

Open3

The Open3 module has a number of different methods to help you chain together different commands along the Unix pipeline, depending on your explicit needs. In this example I used pipeline_r to demonstrate the common scenario of passing the output of the sequence of shell commands to a Ruby method. Give Open3 a perusal and see if it can help you make your tools more versatile.