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?
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.
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.
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.
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}"`
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.
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
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
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'.
"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.
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
Place this at the beginning of your script.rb
#!/usr/bin/env ruby
Then mark it as executable:
chmod +x script.rb
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.
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__)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With