Skip to content
Albert Álef edited this page Jan 24, 2026 · 6 revisions

RubyShell Usage Guide

RubyShell is a Ruby gem that allows shell commands to be called as Ruby methods, providing a natural and intuitive way to execute shell commands from Ruby code.

Installation

Add to your Gemfile:

gem 'rubyshell'

Or install directly:

gem install rubyshell

Then require it in your code:

require 'rubyshell'

Basic Usage

RubyShell extends Kernel with the sh method, making it globally available. There are several ways to use it:

Block Form (DSL)

Execute multiple commands within a block where methods become shell commands:

sh do
  mkdir "my-folder"
  cd "my-folder"
  touch "file.txt"
  pwd
end

Method Chaining

Call commands as methods on the sh object:

sh.mkdir "my-folder"
sh.cd "my-folder"
sh.ls "-la"

result = sh.pwd

Direct Command Form

Pass the command name and arguments directly:

sh(:mkdir, "my-folder")
sh(:ls, "-la")

Extend Module

Import commands directly into your context:

extend RubyShell::Executor

mkdir "my-folder"
cd "my-folder"
ls "-la"

Arguments and Options

Positional Arguments

Pass arguments as strings:

sh.mkdir "my-folder"
sh.cp "source.txt", "dest.txt"
sh.grep "pattern", "file.txt"

Short Flags (Single Character)

Single-character hash keys become -k flags:

sh.ls(l: true)           # ls -l
sh.ls(l: true, a: true)  # ls -l -a
sh.wc(l: true)           # wc -l

Long Flags (Multiple Characters)

Multi-character hash keys become --key flags:

sh.curl("https://example.com", output: "file.html")
# curl https://example.com --output file.html

sh.git("log", oneline: true)
# git log --oneline

Array Values for Repeated Flags

Pass arrays to repeat a flag multiple times:

sh.grep(e: ["error", "warning"])
# grep -e "error" -e "warning"

sh.sed(e: ["s/foo/bar/", "s/baz/qux/"])
# sed -e "s/foo/bar/" -e "s/baz/qux/"

Mixing Arguments and Options

sh.grep("pattern", "file.txt", i: true, n: true)
# grep pattern file.txt -i -n

Command Chaining (Pipes and Redirects)

Using the chain Block

Build pipelines with shell operators:

sh do
  chain { ls | wc("-l") }
end

sh do
  chain { cat("log.txt") | grep("ERROR") | wc("-l") }
end

File Redirection

Redirect output to files:

sh do
  chain { echo("Hello World") >> "output.txt" }  # Append
end

sh do
  chain { ls("-la") > "listing.txt" }  # Overwrite
end

Bang Pattern for Deferred Execution

Commands ending with ! return a Command object without executing:

sh do
  # Build the chain, then execute with .exec
  (ls! | grep!("txt") | wc!("-l")).exec
end

This is useful for:

  • Composing complex pipelines
  • Passing commands as input to other commands
  • Inspecting the command before execution

Inspecting Commands

Use .to_shell to see the shell command string:

sh do
  cmd = ls!(l: true, a: true)
  puts cmd.to_shell  # "ls -l -a"
end

sh do
  chain_cmd = cat!("file.txt") | grep!("pattern")
  puts chain_cmd.to_shell  # "cat file.txt | grep pattern"
end

Piping Data with _stdin

Pass data to a command's stdin using the _stdin option:

String Input

sh.wc(l: true, _stdin: "line1\nline2\nline3\n")
# Returns: "3"

sh.grep("pattern", _stdin: "line with pattern\nother line\n")
# Returns: "line with pattern"

Command Output as Input

Pass a command object (using !) to pipe its output:

sh do
  wc(l: true, _stdin: cat!("file.txt"))
end

sh do
  grep("ERROR", _stdin: tail!(n: 100, _stdin: cat!("app.log")))
  # Similar to: chain { cat('app.log') | tail(n: 100) | grep("ERROR") }
end

Special Commands

cd - Change Directory

Changes Ruby's working directory (affects subsequent commands):

sh.cd "/path/to/directory"
sh.pwd  # Shows new directory

# With block (scoped change)
sh.cd "/tmp" do
  sh.pwd  # /tmp
end
sh.pwd  # Back to original directory

clear - Clear Terminal

Clears the terminal screen:

sh.clear

Error Handling

Commands that fail (non-zero exit code) raise RubyShell::CommandError:

begin
  sh.ls "nonexistent-folder"
rescue RubyShell::CommandError => e
  puts "Command failed: #{e.message}"
  puts "Exit status: #{e.status}"
  puts "Stderr: #{e.stderr}"
  puts "Stdout: #{e.stdout}"
end

Non-existent commands also raise errors:

begin
  sh.nonexistentcommand
rescue RubyShell::CommandError => e
  puts "Command not found: #{e.message}"
end

Return Values

All commands return a StringResult object that behaves like a String:

result = sh.pwd
puts result           # Print the result
result.strip          # String methods work
result.include?("home")  # String comparisons work

lines = sh.ls.split("\n")  # Split into array
count = sh.wc(l: true, _stdin: sh.ls!).to_i  # Convert to integer

Complete Examples

Example 1: File Operations

require 'rubyshell'

sh do
  mkdir "project"
  cd "project"
  touch "README.md"
  chain { echo("# My Project") >> "README.md" }
  cat "README.md"
end

Example 2: Log Analysis

require 'rubyshell'

# Count error lines in logs
sh do
  error_count = chain { cat("app.log") | grep("ERROR") | wc("-l") }.strip

  puts "Found #{error_count} errors"
end

Example 3: Git Operations

require 'rubyshell'

sh do
  # Get current branch
  branch = chain { git("branch") | grep("\\*") }
  puts "Current branch: #{branch}"

  # Count modified files
  modified = chain { git("status", porcelain: true) | wc("-l") }
  puts "Modified files: #{modified.strip}"
end

Example 4: Using Method Chaining

require 'rubyshell'

# Create and populate a directory
sh.mkdir "data"
sh.cd "data"

# Write some test data
sh.chain { sh.echo("test data") >> "test.txt" }

# Process the data
result = sh.chain { sh.cat("test.txt") | sh.wc("-c") }
puts "Character count: #{result.strip}"

Example 5: Complex Pipeline

require 'rubyshell'

sh do
  # Find large files, sort by size, show top 10
  result = chain {
    find(".", type: "f", exec: "ls -s {} ;") |
    sort(n: true, r: true) |
    head(n: 10)
  }
  puts result
end

Tips and Best Practices

  1. Use bang (!) for complex chains - Build your pipeline with ! commands, then call .exec at the end.

  2. Use _stdin for data piping - When you need to pass data to a command, use _stdin instead of shell redirection.

  3. Handle errors - Wrap commands in begin/rescue blocks when failure is possible.

  4. Check commands with .to_shell - Debug your commands by inspecting the shell string before execution.

  5. Use blocks for directory changes - When using cd with a block, the directory change is scoped to that block.

  6. String quoting - RubyShell automatically sanitizes and escapes arguments to prevent shell injection.

Clone this wiki locally