How do I use Ruby for shell scripting?
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
How to call shell commands from Ruby
This explanation is based on a commented Ruby script from a friend of mine. If you want to improve the script, feel free to update it at the link.
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.
Here are ways to execute a shell script:
cmd = "echo 'hi'" # Sample string that can be used
Kernel#`
, commonly called backticks –`cmd`
This is like many other languages, including Bash, PHP, and Perl.
Returns the result (i.e. standard output) of the shell command.
Docs: http://ruby-doc.org/core/Kernel.html#method-i-60
value = `echo 'hi'`
value = `#{cmd}`Built-in syntax,
%x( cmd )
Following the
x
character is a delimiter, which can be any character.
If the delimiter is one of the characters(
,[
,{
, or<
,
the literal consists of the characters up to the matching closing delimiter,
taking account of nested delimiter pairs. For all other delimiters, the
literal comprises the characters up to the next occurrence of the
delimiter character. String interpolation#{ ... }
is allowed.Returns the result (i.e. standard output) of the shell command, just like the backticks.
Docs: https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html#label-Percent+Strings
value = %x( echo 'hi' )
value = %x[ #{cmd} ]Kernel#system
Executes the given command in a subshell.
Returns
true
if the command was found and run successfully,false
otherwise.Docs: http://ruby-doc.org/core/Kernel.html#method-i-system
wasGood = system( "echo 'hi'" )
wasGood = system( cmd )Kernel#exec
Replaces the current process by running the given external command.
Returns none, the current process is replaced and never continues.
Docs: http://ruby-doc.org/core/Kernel.html#method-i-exec
exec( "echo 'hi'" )
exec( cmd ) # Note: this will never be reached because of the line above
Here's some extra advice:$?
, which is the same as $CHILD_STATUS
, accesses the status of the last system executed command if you use the backticks, system()
or %x{}
.
You can then access the exitstatus
and pid
properties:
$?.exitstatus
For more reading see:
- http://www.elctech.com/blog/i-m-in-ur-commandline-executin-ma-commands
- http://blog.jayfields.com/2006/06/ruby-kernel-system-exec-and-x.html
- http://tech.natemurray.com/2007/03/ruby-shell-commands.html
Run .rb (Ruby) file on a Shell Script
I use this when I want want to run Ruby code "as executable".
#!/usr/bin/env ruby
And then chmod the script.
chmod +x script.rb
And run it
./script.rb
I suggest usage of env because running a command through /usr/bin/env
has the benefit of looking for whatever the default version of the program is in your current environment.
Run a shell script from ruby
You can do this a few different ways
Kernel.system "command"
%x[command]
`command`
Run Shell Script from Ruby File and Capture the Output
Don't use system
, it does not capture STDOUT. Use backticks (or %x()
):
output = %x( #{my_script} )
Why and when to use the shell instead of Ruby
The shell's programming language is awful for all but one thing.
Pipelines.
The shell's programming language for pipelines totally rocks.
The |
, &
and ;
operators, plus ()
and ``` form a tidy little language for describing pipelines.
a & b
is concurrent
a ; b
is sequential
a | b
is a pipeline where a feeds b
That part of shell programming rocks.
Think of ( a & b & c ) | tee capture | analysis
as the kind of thing that's hard to express in Python (or Ruby). You can do much of this with iterpipes, but not quite all of it.
Much of the rest you can live without, and use Python (or Ruby) and you'll be happier and more productive.
The biggest thing to watch out for is anything involving expr
at the shell level. As soon as you start trying to do "calculations", you've crossed out of the shell's sweet spot and you should stop programming in the shell and rethink what you're doing.
Shell script for ruby scripts execution
A shell script like this should work:
# Execute Ruby script multiple times with different parameters.
ruby ./analysis.rb -d parameter1 -p parameter2 parameter3
ruby ./analysis.rb -d parameter4 -p parameter5 parameter6
ruby ./analysis.rb -d parameter7 -p parameter8 parameter9
To pass the variables to the Ruby script via the Bash script arguments:
# Include 9 arguments when calling the Bash script. Ruby script will be executed 3 times, with 3 of the arguments each time.
ruby ./analysis.rb $1 $2 $3
ruby ./analysis.rb $4 $5 $6
ruby ./analysis.rb $7 $8 $9
How do I write shell-like scripts using Ruby?
The type of program you require can easily be made with just a few simple constructs.
I know you're not asking for a solution, but I'll just give you a skeleton to start off and play around with:
#!/usr/bin/env ruby
def prnthelp
puts "Hello sir, what would you like to do?"
puts "1: dir"
puts "2: exit"
end
def loop
prnthelp
case gets.chomp.to_i
when 1 then puts "you chose dir!"
when 2 then puts "you chose exit!"
exit
end
loop
end
loop
Anyways, this is a simplistic example on how you could do it, but probably the book recommended in the comments is better. But this is just to get you off.
Some commands to get you started are:
somevar = gets
This gets user input. Maybe learn about some string methods to manipulate this input can do you some good. http://ruby-doc.org/core-2.0/String.htmlchomp
will chop off any whitespace, and to_i
converts it to an integer.
Some commands to do Unix stuff:
system('ls -la') #=> outputs the output of that command
exit #=> exits the program
Anyways, if you want this kind of stuff, I think it's not a bad idea to look into http://www.codecademy.com/ basically they teach you Ruby by writing small scripts such as these. However, they maybe not be completely adapted to Unix commands, but user input and the likes are certainly handled.
Edit:
As pointed out do use this at the top of your script:
#!/usr/bin/env ruby
Edit:
Example of chomp
vs. chop
:
full_name = "My Name is Ravikanth\r\n"
full_name.chop! # => "My Name is Ravikanth"
Now if you run chop
and there are no newline characters:
puts full_name #=> "My Name is Ravikanth"
full_name.chop! #=> "My Name is Ravikant"
versus:
puts full_name #=> "My Name is Ravikanth\r\n"
full_name.chomp! #=> "My Name is Ravikanth"
full_name.chomp! #=> "My Name is Ravikanth"
See: "Ruby Chop vs Chomp"
Switching rubies in shell script
I ended up adding this to the line before:
[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm"
as explained in this thread (sorry - though I was aware of this thread before, I didn't quite grasp it):
RVM doesn't switch Rubies
Related Topics
Optimization for Finding Perfect-Square Algorithm
Rails: Good Rspec2 Example Usage? (Also: Cucumber, Pickle, Capybara)
When Do We Use the "||=" Operator in Rails? What Is Its Significance
Rails 3 - Restricting Formats for Action in Resource Routes
Undefined Method '>' for Nil:Nilclass <Nomethoderror>
Ruby Koans: Explicit Scoping on a Class Definition Part 2
Is Ruby on Rails (Or at Least the Community) Dying
Open_Id_Authentication - "Openidauthentication.Store Is Nil. Using In-Memory Store." Problem
How to Specify Local .Gem Files in My Gemfile
What's the Difference Between Request.Remote_Ip and Request.Ip in Rails
Can't Install Ffi -V '1.9.18' on MACos Catalina
What Is the Use of "#!/Usr/Local/Bin/Ruby -W" at the Start of a Ruby Program