How do I use Ruby for shell scripting?

I have some simple shell scripting tasks that I want to do

For example: Selecting a file in the working directory from a list of the files matching some regular expression.

I know that I can do this sort of thing using standard bash and grep but I would be nice to be able to hack quick scripts that will work in windows and linux without me having to memorize a heap of command line programs and flags etc.

I tried to get this going but ended up getting confused about where I should be getting information such as a reference to the current directory

So the question is what parts of the Ruby libraries do I need to know to write ruby shell scripts?


By default, you already have access to Dir and File, which are pretty useful by themselves.

Dir['*.rb'] #basic globs
Dir['**/*.rb'] #** == any depth of directory, including current dir.
#=> array of relative names

File.expand_path('~/file.txt') #=> "/User/mat/file.txt"
File.dirname('dir/file.txt') #=> 'dir'
File.basename('dir/file.txt') #=> 'file.txt'
File.join('a', 'bunch', 'of', 'strings') #=> 'a/bunch/of/strings'

__FILE__ #=> the name of the current file

Also useful from the stdlib is FileUtils

require 'fileutils' #I know, no underscore is not ruby-like
include FileUtils
# Gives you access (without prepending by 'FileUtils.') to
cd(dir, options)
cd(dir, options) {|dir| .... }
pwd()
mkdir(dir, options)
mkdir(list, options)
mkdir_p(dir, options)
mkdir_p(list, options)
rmdir(dir, options)
rmdir(list, options)
ln(old, new, options)
ln(list, destdir, options)
ln_s(old, new, options)
ln_s(list, destdir, options)
ln_sf(src, dest, options)
cp(src, dest, options)
cp(list, dir, options)
cp_r(src, dest, options)
cp_r(list, dir, options)
mv(src, dest, options)
mv(list, dir, options)
rm(list, options)
rm_r(list, options)
rm_rf(list, options)
install(src, dest, mode = <src's>, options)
chmod(mode, list, options)
chmod_R(mode, list, options)
chown(user, group, list, options)
chown_R(user, group, list, options)
touch(list, options)

Which is pretty nice


As the others have said already, your first line should be

#!/usr/bin/env ruby

And you also have to make it executable: (in the shell)

chmod +x test.rb

Then follows the ruby code. If you open a file

File.open("file", "r") do |io|
    # do something with io
end

the file is opened in the current directory you'd get with pwd in the shell.

The path to your script is also simple to get. With $0 you get the first argument of the shell, which is the relative path to your script. The absolute path can be determined like that:

#!/usr/bin/env ruby
require 'pathname'
p Pathname.new($0).realpath()

For file system operations I almost always use Pathname. This is a wrapper for many of the other file system related classes. Also useful: Dir, File...


Here's something important that's missing from the other answers: the command-line parameters are exposed to your Ruby shell script through the ARGV (global) array.

So, if you had a script called my_shell_script:

#!/usr/bin/env ruby
puts "I was passed: "
ARGV.each do |value|
  puts value
end

...make it executable (as others have mentioned):

chmod u+x my_shell_script

And call it like so:

> ./my_shell_script one two three four five

You'd get this:

I was passed: 
one
two
three
four
five

The arguments work nicely with filename expansion:

./my_shell_script *

I was passed: 
a_file_in_the_current_directory
another_file    
my_shell_script
the_last_file

Most of this only works on UNIX (Linux, Mac OS X), but you can do similar (though less convenient) things in Windows.


There's a lot of good advice here, so I wanted to add a tiny bit more.

  1. Backticks (or back-ticks) let you do some scripting stuff a lot easier. Consider

    puts `find . | grep -i lib`
    
  2. If you run into problems with getting the output of backticks, the stuff is going to standard err instead of standard out. Use this advice

    out = `git status 2>&1`
    
  3. Backticks do string interpolation:

    blah = 'lib'
    `touch #{blah}`
    
  4. You can pipe inside Ruby, too. It's a link to my blog, but it links back here so it's okay :) There are probably more advanced things out there on this topic.

  5. As other people noted, if you want to get serious there is Rush: not just as a shell replacement (which is a bit too zany for me) but also as a library for your use in shell scripts and programs.


On Mac, Use Applescript inside Ruby for more power. Here's my shell_here script:

#!/usr/bin/env ruby
`env | pbcopy` 
cmd =  %Q@tell app "Terminal" to do script "$(paste_env)"@
puts cmd

`osascript -e "${cmd}"`