How to Run System Commands From Ruby

If you want to run an external command from Ruby…

…like wkhtmltopdf to convert an HTML file into a PDF.

There are a few Ruby methods you can use.

Depending on the method you use you’ll get different results.

Let’s explore these methods together!

The Ruby System Method

The Ruby system method is the simplest way to run an external command.

It looks like this:

system("ls")

Notice that system will print the command output as it happens.

Also system will make your Ruby program wait until the command is done.

Try this:

system("sleep 2")

There are ways to run commands in the background as we’ll see later.

System can return 3 possible values:

  • true if the command worked
  • false if the command returns an error code
  • nil if command execution fails (command not found)

You can get the exit status code of the last external command that you ran with the $? global variable. This status code can give you more information about why the command failed.

What is Shell Expansion?

When you run an external command with system you’ll get the “shell expansion” effect.

Shell expansion is when the shell (sh/bash/zsh) takes some special characters (*, ?, and [) from your command & expands them into a file list.

Example:

system("echo *")

This will print every file & folder in the current directory.

The * is replaced with the list of files before the command runs.

If you want to use these special characters as they are pass the arguments as the 2nd argument of system.

Like this:

system("echo", "*")

Now the output will be *.

How to Use Environment Variables

If you want to pass a specific environment variable to an external command you can use a hash as the first argument.

Here’s an example:

system({"rubyguides" => "best"}, "ruby", "-e p ENV['rubyguides']")

# "best"

This will not change the current environment of your Ruby application.

%x / Kernel#`

If you want to get the output from the command you’re running, instead of displaying it then you can use %x or the Kernel#` method.

They do the same thing.

Here’s an example:

`ls`

Another with %x:

%x|ls|

These two examples will return a string with the output of the ls command.

Notice that you still have to wait for the command to finish unless you run it inside a thread.

How to Use Fork + Exec To Run External Commands On a Separate Process

Forking makes a copy of your current process (your Ruby app) then you can replace that copy with another process using exec.

Note: The fork method is not available on Windows.

This is a popular pattern in the Unix world.

Here’s how it works:

fork { exec("ls") }

This will run ls on another process & display its output.

Because this command is running in another process it will not block your Ruby app from running like the system method or %x.

Important:

If you use exec without fork you’re going to replace your current process.

This means your Ruby program will end.

How to Use the Popen Method For Two Way Communication With An External Program

If you need:

  • More control over the process
  • Two-way communication

Then the IO.popen method is what you are looking for.

In the following example I use the popen method to launch an irb process, then send some input to it & read the output.

Here the example:

r = IO.popen("irb", "r+")

r.write "puts 123 + 1\n"

3.times { puts r.gets }

r.write "exit\n"

This r variable is an IO object, this means that you can write & read from it like a regular file.

Using popen also means that your external command will run on its own process, so you don’t have to run it on a thread.

There is also a popen3 method in the standard library.

The difference with the regular popen is that it will split regular output & error messages into separate IO objects.

Conclusion

You have learned about the different ways that you can interact with external commands in Ruby.

Be careful with these!

Don’t pass any kind of user input into these commands unless you want a security issue in your Ruby app.

If you learned something new please share this article so more people can see it.

Thanks for reading 🙂

2 thoughts on “How to Run System Commands From Ruby”

  1. Now, i understand, why my rails app was terminated, when i invoked exec from controller action without fork. Thanks!

Comments are closed.