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
endAs 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.
-
runRun the command and wait for the result. Returns aCommandRunResultinstance. -
executeSame asrun, but terser, with no additional output added to STDOUT. -
run_as_replacementRun the command in a new process and kill this process. -
run_detachedRun the command as a new background process. It can continue even if the caller terminates. -
run_in_backgroundRun 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:
-
stdoutA String containing the contents of STDOUT. -
stderrA String containing the contents of STDERR. -
exit_codeThe exit code of the command -
succeded?True ifexit_codeis 0. -
must_succeed!Callsfailwith 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.