How to Use Ruby for Shell Scripting

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
  1. 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}`
  2. 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} ]
  3. 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 )
  4. 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.html
chomp 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



Leave a reply



Submit