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:
- http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html
- 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!