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?
-
3Probably not a good answer, but Practical Ruby for System Administration is a great reference. amazon.com/Practical-System-Administration-Experts-Source/dp/…exiquio– exiquio2012年08月30日 19:38:55 +00:00Commented Aug 30, 2012 at 19:38
13 Answers 13
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}"`
-
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 Lizard2010年03月06日 13:52:34 +00:00Commented Mar 6, 2010 at 13:52
-
@Bill the Lizard, yeah, that was the 'trick' I was needing: the double indents. THANKS FOR THE HELP.Dan Rosenstark– Dan Rosenstark2010年03月06日 14:28:58 +00:00Commented Mar 6, 2010 at 14:28
-
1I think Rush is dead: groups.google.com/group/ruby-shell/browse_thread/thread/…Dan Rosenstark– Dan Rosenstark2011年11月29日 16:40:54 +00:00Commented Nov 29, 2011 at 16:40
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.
-
2Good 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– Sébastien RoccaSerra2010年03月16日 11:09:47 +00:00Commented Mar 16, 2010 at 11:09
-
I think the book have some good information but to much overhead for experienced programmers.The D Merged– The D Merged2014年01月08日 07:16:43 +00:00Commented Jan 8, 2014 at 7:16
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
-
2Rush: no. Why? groups.google.com/group/ruby-shell/browse_thread/thread/… It's great but there's no one at the wheel.Dan Rosenstark– Dan Rosenstark2010年04月04日 09:56:54 +00:00Commented Apr 4, 2010 at 9:56
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:
They give you a quick start to write your own scripts, especially 'command line app'.
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
"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.
Place this at the beginning of your script.rb
#!/usr/bin/env ruby
Then mark it as executable:
chmod +x script.rb
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__)