Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 878
Willbill Avatar asked Oct 03 '08 11:10

Willbill


People also ask

How do I run a shell script in Ruby?

First, note that when Ruby calls out to a shell, it typically calls /bin/sh , not Bash. Some Bash syntax is not supported by /bin/sh on all systems. This is like many other languages, including Bash, PHP, and Perl. Returns the result (i.e. standard output) of the shell command.

How do I make a Ruby script executable?

You can make the script executable with the following command: chmod +x hello. rb . chmod is a shell command that allows us to change the permissions for a file. The +x specifies that the script should be executable.


13 Answers

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

like image 65
webmat Avatar answered Oct 04 '22 05:10

webmat


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

like image 35
Georg Schölly Avatar answered Oct 04 '22 07:10

Georg Schölly


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.

like image 36
Craig Walker Avatar answered Oct 04 '22 06:10

Craig Walker


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}"`
like image 38
Dan Rosenstark Avatar answered Oct 04 '22 06:10

Dan Rosenstark


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.

like image 22
Aaron Hinni Avatar answered Oct 04 '22 07:10

Aaron Hinni


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

like image 22
craigp Avatar answered Oct 04 '22 07:10

craigp


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

like image 27
Vasil Avatar answered Oct 04 '22 05:10

Vasil


When you want to write more complex ruby scripts, these tools may help:

For example:

  • thor(a scripting framework)

  • gli(git like interface)

  • methadone(for creating simple tools)

They give you a quick start to write your own scripts, especially 'command line app'.

like image 38
Bohr Avatar answered Oct 04 '22 06:10

Bohr


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

like image 39
Matthew Scharley Avatar answered Oct 04 '22 07:10

Matthew Scharley


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
like image 32
Houcheng Avatar answered Oct 04 '22 07:10

Houcheng


Place this at the beginning of your script.rb

#!/usr/bin/env ruby

Then mark it as executable:

chmod +x script.rb
like image 23
Daniel Beardsley Avatar answered Oct 04 '22 06:10

Daniel Beardsley


The answer by webmat is perfect. I just want to point you to a addition. If you have to deal a lot with command line parameters for your scripts, you should use optparse. It is simple and helps you tremendously.

like image 37
awenkhh Avatar answered Oct 04 '22 07:10

awenkhh


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__)
like image 28
bltxd Avatar answered Oct 04 '22 06:10

bltxd