ruby 35 lines · 7 steps

Building safe shell commands in Ruby

An ArchiveBuilder assembles a tar command from parts, escapes it, and runs it while checking the exit status.

Explained by highlit
1require "shellwords"
2require "open3"
3 
4module Backup
5 class ArchiveBuilder
6 def initialize(source_dir, destination:, exclude: [])
7 @source_dir = source_dir
8 @destination = destination
9 @exclude = exclude
10 end
11 
12 def call
13 stdout, stderr, status = Open3.capture3(build_command)
14 unless status.success?
15 raise "tar failed (#{status.exitstatus}): #{stderr.strip}"
16 end
17 stdout
18 end
19 
20 private
21 
22 def build_command
23 parts = ["tar", "--create", "--gzip"]
24 parts.concat(exclude_flags)
25 parts << "--file" << @destination
26 parts << "--directory" << @source_dir
27 parts << "."
28 Shellwords.join(parts)
29 end
30 
31 def exclude_flags
32 @exclude.flat_map { |pattern| ["--exclude", pattern] }
33 end
34 end
35end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Assembling commands as an array and joining with Shellwords avoids injection from unescaped paths or patterns.
  2. 2Open3.capture3 gives you stdout, stderr, and exit status separately so you can react to failures precisely.
  3. 3Always check status.success? rather than trusting a subprocess to have done its job silently.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building safe shell commands in Ruby — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code