-
Notifications
You must be signed in to change notification settings - Fork 5
Home
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.
Add to your Gemfile:
gem 'rubyshell'Or install directly:
gem install rubyshellThen require it in your code:
require 'rubyshell'RubyShell extends Kernel with the sh method, making it globally available. There are several ways to use it:
Execute multiple commands within a block where methods become shell commands:
sh do
mkdir "my-folder"
cd "my-folder"
touch "file.txt"
pwd
endCall commands as methods on the sh object:
sh.mkdir "my-folder"
sh.cd "my-folder"
sh.ls "-la"
result = sh.pwdPass the command name and arguments directly:
sh(:mkdir, "my-folder")
sh(:ls, "-la")Import commands directly into your context:
extend RubyShell::Executor
mkdir "my-folder"
cd "my-folder"
ls "-la"Pass arguments as strings:
sh.mkdir "my-folder"
sh.cp "source.txt", "dest.txt"
sh.grep "pattern", "file.txt"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 -lMulti-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 --onelinePass 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/"sh.grep("pattern", "file.txt", i: true, n: true)
# grep pattern file.txt -i -nBuild pipelines with shell operators:
sh do
chain { ls | wc("-l") }
end
sh do
chain { cat("log.txt") | grep("ERROR") | wc("-l") }
endRedirect output to files:
sh do
chain { echo("Hello World") >> "output.txt" } # Append
end
sh do
chain { ls("-la") > "listing.txt" } # Overwrite
endCommands ending with ! return a Command object without executing:
sh do
# Build the chain, then execute with .exec
(ls! | grep!("txt") | wc!("-l")).exec
endThis is useful for:
- Composing complex pipelines
- Passing commands as input to other commands
- Inspecting the command before execution
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"
endPass data to a command's stdin using the _stdin option:
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"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") }
endChanges 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 directoryClears the terminal screen:
sh.clearCommands 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}"
endNon-existent commands also raise errors:
begin
sh.nonexistentcommand
rescue RubyShell::CommandError => e
puts "Command not found: #{e.message}"
endAll 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 integerrequire 'rubyshell'
sh do
mkdir "project"
cd "project"
touch "README.md"
chain { echo("# My Project") >> "README.md" }
cat "README.md"
endrequire 'rubyshell'
# Count error lines in logs
sh do
error_count = chain { cat("app.log") | grep("ERROR") | wc("-l") }.strip
puts "Found #{error_count} errors"
endrequire '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}"
endrequire '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}"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-
Use bang (
!) for complex chains - Build your pipeline with!commands, then call.execat the end. -
Use
_stdinfor data piping - When you need to pass data to a command, use_stdininstead of shell redirection. -
Handle errors - Wrap commands in begin/rescue blocks when failure is possible.
-
Check commands with
.to_shell- Debug your commands by inspecting the shell string before execution. -
Use blocks for directory changes - When using
cdwith a block, the directory change is scoped to that block. -
String quoting - RubyShell automatically sanitizes and escapes arguments to prevent shell injection.