One of the things that is nice about Ruby is the ability to use it for scripting. Ruby makes it nice and easy to shell out to run standard *nix commands.
The problem is, the nice simple way that seems to be the most common, is also the least safe. I’m looking at you backticks.
#!/usr/env ruby result = `gerp -e 'some regex' foo` puts "<time to process result>" puts "All Good"
And when we run the above, we see that our script completed, even though we really bombed out trying to run
gerp instead of
sh: gerp: command not found <time to process result> All Good
Sure we get a warning
gerp: command not found, but the script still proceeds to plow ahead and do any other side effects that it is setup to do, although something went wrong earlier.
This has become one of the biggest thorns in my side at work.
Lucky the technical solution to this is straight forward, so I want to share it with readers of this blog so you can stop making the mistake of using backticks to shell out commands, and use a solution that does not cover up issues in production.
#!/usr/env ruby require 'open3' def execute_syscall(cmd) results, error, status = Open3.capture3(cmd) raise error unless status.success? results end result = execute_syscall "gerp -e 'some regex' foo" puts "<time to process result>" puts "All Good"
First we require
open3. Open3 allows us to be able to capture the results, the error stream, and the status. This way, we can check if the status of the command was anything other than success, and if not, we raise the error we get from STDERR.
Now when we run it, the script stops in its tracks. Not only that, but our script returns a failure error code as well.
> ruby script_demo.rb script_demo.rb:7:in `execute_syscall': sh: gerp: command not found (RuntimeError) from script_demo.rb:12:in `<main>' > echo $? 1
That way we can know that something went wrong in our program, especially when it is setup as a cron job or as some other unsupervised task.
Hope this can save you some headaches and frustration on your end as well.