How do I use Ruby for shell scripting?
RubyShellScriptingRuby Problem Overview
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?
Ruby Solutions
Solution 1 - Ruby
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
Solution 2 - Ruby
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...
Solution 3 - Ruby
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.
Solution 4 - Ruby
There's a lot of good advice here, so I wanted to add a tiny bit more.
-
Backticks (or back-ticks) let you do some scripting stuff a lot easier. Consider
puts `find . | grep -i lib`
-
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`
-
Backticks do string interpolation:
blah = 'lib' `touch #{blah}`
-
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.
-
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}"`
Solution 5 - Ruby
Go get yourself a copy of Everyday Scripting with Ruby. It has plenty of useful tips on how to do the types of things your are wanting to do.
Solution 6 - Ruby
let's say you write your script.rb
script. put:
#!/usr/bin/env ruby
as the first line and do a chmod +x script.rb
Solution 7 - Ruby
This might also be helpful: http://rush.heroku.com/
I haven't used it much, but looks pretty cool
From the site:
> rush is a replacement for the unix shell (bash, zsh, etc) which uses pure Ruby syntax. Grep through files, find and kill processes, copy files - everything you do in the shell, now in Ruby
Solution 8 - Ruby
When you want to write more complex ruby scripts, these tools may help:
For example:
They give you a quick start to write your own scripts, especially 'command line app'.
Solution 9 - Ruby
"How do I write ruby" is a little beyond the scope of SO.
But to turn these ruby scripts into executable scripts, put this as the first line of your ruby script:
#!/path/to/ruby
Then make the file executable:
chmod a+x myscript.rb
and away you go.
Solution 10 - Ruby
The above answer are interesting and very helpful when using Ruby as shell script. For me, I does not use Ruby as my daily language and I prefer to use ruby as flow control only and still use bash to do the tasks.
Some helper function can be used for testing execution result
#!/usr/bin/env ruby
module ShellHelper
def test(command)
`#{command} 2> /dev/null`
$?.success?
end
def execute(command, raise_on_error = true)
result = `#{command}`
raise "execute command failed\n" if (not $?.success?) and raise_on_error
return $?.success?
end
def print_exit(message)
print "#{message}\n"
exit
end
module_function :execute, :print_exit, :test
end
With helper, the ruby script could be bash alike:
#!/usr/bin/env ruby
require './shell_helper'
include ShellHelper
print_exit "config already exists" if test "ls config"
things.each do |thing|
next if not test "ls #{thing}/config"
execute "cp -fr #{thing}/config_template config/#{thing}"
end
Solution 11 - Ruby
Place this at the beginning of your script.rb
#!/usr/bin/env ruby
Then mark it as executable:
chmod +x script.rb
Solution 12 - Ruby
In ruby, the constant __FILE__
will always give you the path of the script you're running.
On Linux, /usr/bin/env
is your friend:
#! /usr/bin/env ruby
# Extension of this script does not matter as long
# as it is executable (chmod +x)
puts File.expand_path(__FILE__)
On Windows it depends whether or not .rb files are associated with ruby. If they are:
# This script filename must end with .rb
puts File.expand_path(__FILE__)
If they are not, you have to explicitly invoke ruby on them, I use a intermediate .cmd file:
my_script.cmd:
@ruby %~dp0\my_script.rb
my_script.rb:
puts File.expand_path(__FILE__)