Executing shell script from Ruby code

Many times when we are developing an application using Ruby, we want to run a shell script to do certain task. For example, imagine that I have a script do_something.py which performs a very complex calculation and returns the result. From the extension, you can easily guess that it is written in Python. What if our Ruby application needs the output from this do_something.py script? We have to find a way to call this script from Ruby app. It is not too hard though.

Ruby supports us running shell script in several ways

1. Using system function

This function is a great way to run if you do not need to worry about the result returned by the script. The output of system function will always be true or nil depending on whether or not the script has been executed without error. Therefore, every error while executing the script will not be passed to our application. For example:

system("abcdad") # This will return nil
system("ls") # This will return true

Notice that when executing the script using system function, the Ruby app will stop and wait for the command to be completed before continuing (synchronization mechanism)

2. Using backtick

Backtick is the ` character (it is usually located under the Esc key in the keyboard). Unlike system function, we can get the output of the script using this character. However, in case of exception, our program will also crash (the exception is passed onto the Ruby program). For example:

`abcdad` # Exception here, Ruby app will crash with detailed error
`ls` # list of files will be shown

Similar to system function, when calling the script using backtick, the Ruby app will also stop executing and wait for the result from the script

3. Using exec

This should not be used in normal Ruby app because it will exit the current Ruby process and run the command provided in exec function. For example:

exec("ls") # this will stop the current Ruby script and automatically run "ls" command as new process

4. Using sh

This function basically calls system function under the hood, but it provides an easy way to check the status of the execution.

5. Using Process.spawn

This function will spawn new process to executing the script and return the process id. For example:

Process.spawn("ls") # this will return immediately the id of the process executing the 'ls' command

For more details about these functions (and others), I recommend reading further in following posts:

  1. http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html
  2. http://makandracards.com/makandra/1243-execution-of-shell-code-in-ruby-scripts

IMPORTANT

Executing any system command is quite dangerous, especially if your Ruby app needs values from outside. For example, imagine that my do_something.py script needs two numbers as arguments. I would execute the script like this

`python do_something.py #{a} #{b}`

Here a and b are two variables with value provided by user. If we do not pre-process the value of a or b before executing the command, we are actually inviting bad guys to destroy our server. Let me give you an example: assume that I now assign my variable like this

a = '5 10 && rm -rf / `
b = ''

So let's replace a and b with actual values, we would get the following command

python do_something.py 5 10 && rm -rf /

Do you see that? My command now consists of two sub commands, one is an usual command we expected to be executed, another command is to delete everything on our server.

So, be careful with your execution, always pre-process variable values and make sure no malicious script is allowed to execute.

Happy coding!