Caliph
Caliph - a Ruby tool for generating and executing command-line commands.
Does your Ruby script or app need to generate commands to run at the CLI, to run with system() or similar? Want to generate them in a clean, testable Ruby api? How about automatically capturing output, exit code, and other goodies?
Because doing the system programming to accomplish all this is really kind of a pain, we've bundled all of these into a single gem.
Usage Examples
# An elaborate way to print a file listing:
shell = Caliph.new
long_listing = shell.run("ls", "-la")
if long_listing.succeeded?)
puts long_listing.stdout
end
As ever, it's a contrived example, but you can see that you use {Caliph.new} to create a shell, and then call {Caliph::Shell#run} (or it's cousins) to build and execute commands, and then use methods on {Caliph::CommandRunResult} to examine their status and results. Pretty simple.
Create a command
# 'ls -la'
Caliph::CommandLine.new('ls', '-la')
# 'ls -la', another way
cmd = Caliph::CommandLine.new('ls')
cmd.options << '-la'
# Of course, there's a DSL
include Caliph::CommandLineDSL
# synonymous with Caliph::CommandLine.new('cat', '/etc/passwd')
cmd('cat', '/etc/passwd')
Add environment variables
# RAILS_ENV=production rake db:migrate
command = cmd('rake', 'db:migrate')
command.env['RAILS_ENV'] = 'production
Run any of the above by giving them to a shell
shell.run(command)
# Shell yields commands for configuration prior to running
shell.run("rake", "db:migrate") do |migrate|
migrate.env['RAILS_ENV'] = "production"
migrate.redirect_stdout("/tmp/prod.log")
end
See commands as they'd be run
{Caliph::CommandLine#string_format} or #to_s
returns the entire command.
{Caliph::CommandLine#command} returns just the command portion without
prepended environment variables, which might be handy if you're passing the
command to ssh or sudo and need to handle ENV differently.
command = cmd('java', 'my_file.jar')
command.env['JAVA_HOME'] = '~/java_files'
command.string_format # => "JAVA_HOME='~/java_files' java my_file.jar"
command.command # => "java my_file.jar"
Chaining commands
{Caliph::CommandChain} and related classes implement chained commands. If you've mixed in {Caliph::CommandLineDSL}, you can use operators &, |, and - for conditional, pipe, and path-chaining, respectively.
# Pipe Chain
# find . -name '*.sw.' | xargs rm
cmd('find', '.', "-name '*.sw.'") | cmd('xargs', 'rm')
# && - style conditional chain
# cd /tmp/trash && rm -rf *
cmd("cd", "/tmp/trash") & %w{rm -rf *}
# Double-hyphen separated commands
# sudo -- gem install bundler
cmd("sudo") - ["gem", "install", "bundler"]
Redirecting Output
# redirect STDOUT
# echo "foo" 1>some_file.txt
cmd('echo').redirect_stdout('some_file.txt')
# redirect STDERR
# echo "foo" 2>error_file.txt
cmd('echo').redirect_stderr('error_file.txt')
# chain redirects
# curl http://LRDesign.com 1>page.html 2>progress.txt
cmd('curl', 'http://LRDesign.com').redirect_stdout('page.html').redirect_stderr('progress.txt')
# redirect STDOUT and STDERR to the same destination with one command
# rm -rf 1>/dev/null 2>/dev/null
cmd('rm', '-rf').redirect_both('/dev/null')
Execute commands and capture the output
Several instance methods on {CommandLine} and {CommandChain} are provided for executing commands.
-
run
Run the command and wait for the result. Returns aCommandRunResult
instance. -
execute
Same asrun
, but terser, with no additional output added to STDOUT. -
run_as_replacement
Run the command in a new process and kill this process. -
run_detached
Run the command as a new background process. It can continue even if the caller terminates. -
run_in_background
Run the command as a background process, but kill it if the caller terminates
# find all vim swap files and wait for result
results = shell.run(cmd("find", '.', "-name *.sw.'"))
# delete all vim swap files in a parallel process
find_swaps = cmd('find', '.', "-name '*.sw.'") | cmd('xargs', 'rm')
shell.run_in_background(find_swaps)
# launch a server, terminating this process. Useful for wrapper scripts!
launcher = cmd('pg_ctl', 'start -l', 'logfile')
shell.run_as_replacement(launcher)
Examine the results of a command
{Caliph::Shell#execute} returns a {Caliph::CommandRunResult} instance. {CommandRunResult} has the following useful instance methods:
-
stdout
A String containing the contents of STDOUT. -
stderr
A String containing the contents of STDERR. -
exit_code
The exit code of the command -
succeded?
True ifexit_code
is 0. -
must_succeed!
Callsfail
with an error message if the command did not exit successfully.
Testing code that uses Caliph
Caliph includes some useful classes for mocking out the command line environment for purposes of testing. See {Caliph::MockCommandResult} and {Caliph::CommandLine} in lib/caliph/testing for more info.
Further documentation on testing coming soon!
Acknowledgements
The first version of these classes were originally written as part of Mattock by Judson Lester: https://github.com/nyarly/mattock
Credits
Evan Dorn and Judson Lester of Logical Reality Design, Inc.