views:

6393

answers:

10

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 ect.

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?

+1  A: 

"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.

Matthew Scharley
+1  A: 

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

Vasil
A: 

Place this at the beginning of your script.rb

#!/usr/bin/env ruby

Then mark it as executable:

chmod +x script.rb
Daniel Beardsley
+11  A: 

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...

Georg
+1 concise and useful stuff.
Yar
+1  A: 

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__)
bltxd
+7  A: 

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.

Aaron Hinni
Good book, I'm reading it right now: it feels like a zen code journey. And if you don't know TDD, you'll learn TDD's basics allong the way.
Sébastien RoccaSerra
+21  A: 

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

webmat
+6  A: 

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

Deviant
I second Rush. I often need to write one-off "prototype" scripts for various things that need doing, and many times the scripts grow into complex enough systems to justify converting it from shell to Ruby. With Rush, these kinds of formerly scripty-things are now easier to pull into larger Ruby systems.
korch
Rush: no. Why? http://groups.google.com/group/ruby-shell/browse_thread/thread/75c8cea757d2f680# It's great but there's no one at the wheel.
Yar
+5  A: 

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.

Craig Walker
+5  A: 

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>`
    
  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.

Yar
I just had to indent the code 4 more spaces for them to be formatted. I also put the backticks back, but I don't know Ruby very well at all, so you'll want to check to make sure it's what you intended.
Bill the Lizard
@Bill the Lizard, yeah, that was the 'trick' I was needing: the double indents. THANKS FOR THE HELP.
Yar