How to call shell commands from Ruby
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
Ruby gives you several process APIs, and they are not interchangeable. The right choice depends on whether you need a simple success flag, captured output, streaming control, or full replacement of the current process. The main engineering concern is safety: avoid building one large shell string when you can pass command arguments directly.
Use system for Simple Execution
system runs a command and returns true on success, false on non-zero exit, or nil if the command could not start.
This is a good fit when the child process should inherit your terminal and you only care whether it worked.
Use Backticks or %x When You Need Stdout
Backticks and %x capture standard output into a string. They are convenient, but they invoke a shell when given shell syntax, so they are easy to misuse with untrusted input.
These forms are acceptable for quick scripts under your control. They are a poor choice when command arguments come from users or external data.
Use Open3 for Output and Error Streams
Open3 is the most practical standard-library option when you need stdout, stderr, and exit status separately.
This is usually the right API for automation tasks because it gives you structured results without forcing you to parse mixed console output.
Pass Arguments as Separate Values
If any part of the command is dynamic, use the array-style argument form. That avoids shell expansion and greatly reduces command-injection risk.
Compare that with a dangerous pattern such as interpolating filename into one shell string. The separate-argument form treats the value as data, not shell syntax.
Use spawn for Long-Running Background Work
spawn starts a process and returns its process id immediately. You can then wait for it or let it continue independently.
This is useful when you want manual process control or need to connect pipes yourself.
Use exec Only When Replacing the Current Process
exec does not create a child that Ruby continues to manage. It replaces the current Ruby process entirely.
That is appropriate in wrappers or launch scripts, but it surprises people who expect code after exec to run.
A Safe Helper Method
Many applications benefit from a small wrapper that fails loudly and returns structured data.
A wrapper like this keeps subprocess handling consistent across the codebase.
Common Pitfalls
The most common mistake is interpolating user input into a shell string. That turns data into executable syntax and creates injection bugs. Another problem is using backticks when you really need stderr or an exit code, which leads to fragile error handling.
It is also easy to forget that system writes directly to the terminal unless you redirect it, while Open3.capture3 buffers output in memory. For very large outputs, streaming with pipes may be better than capturing everything at once. Finally, remember that exec replaces the current process, so any cleanup logic after it will never run.
Summary
- use
systemwhen you only need success or failure - use backticks or
%xonly for simple stdout capture in trusted scripts - use
Open3.capture3when you need stdout, stderr, and exit status separately - pass command arguments as separate values instead of one shell string
- use
spawnfor background processes andexeconly to replace the current process
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.