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. << '-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: github.com/nyarly/mattock
Credits
Evan Dorn and Judson Lester of Logical Reality Design, Inc.