A small library for doing (command) lines.
The basic, normal stuff:
line = Cocaine::CommandLine.new("echo", "hello 'world'")
line.command # => "echo hello 'world'"
line.run # => "hello world\n"
Interpolated arguments:
line = Cocaine::CommandLine.new("convert", ":in -scale :resolution :out",
:in => "omg.jpg",
:resolution => "32x32",
:out => "omg_thumb.jpg")
line.command # => "convert 'omg.jpg' -scale '32x32' 'omg_thumb.jpg'"
It prevents attempts at being bad:
line = Cocaine::CommandLine.new("cat", ":file", :file => "haha`rm -rf /`.txt")
line.command # => "cat 'haha`rm -rf /`.txt'"
line = Cocaine::CommandLine.new("cat", ":file", :file => "ohyeah?'`rm -rf /`.ha!")
line.command # => "cat 'ohyeah?'\\''`rm -rf /`.ha!'"
You can ignore the result:
line = Cocaine::CommandLine.new("noisy", "--extra-verbose", :swallow_stderr => true)
line.command # => "noisy --extra-verbose 2>/dev/null"
# ... and on Windows...
line.command # => "noisy --extra-verbose 2>NUL"
If your command errors, you get an exception:
line = Cocaine::CommandLine.new("git", "commit")
begin
line.run
rescue Cocaine::ExitStatusError => e
e.message # => "Command 'git commit' returned 1. Expected 0"
end
If your command might return something non-zero, and you expect that, it's cool:
line = Cocaine::CommandLine.new("/usr/bin/false", "", :expected_outcodes => [0, 1])
begin
line.run
rescue Cocaine::ExitStatusError => e
# => You never get here!
end
You don't have the command? You get an exception:
line = Cocaine::CommandLine.new("lolwut")
begin
line.run
rescue Cocaine::CommandNotFoundError => e
e # => the command isn't in the $PATH for this process.
end
But don't fear, you can specify where to look for the command:
Cocaine::CommandLine.path = "/opt/bin"
line = Cocaine::CommandLine.new("lolwut")
line.command # => "lolwut", but it looks in /opt/bin for it.
You can even give it a bunch of places to look:
FileUtils.rm("/opt/bin/lolwut")
File.open('/usr/local/bin/lolwut') {|f| f.write('echo Hello') }
Cocaine::CommandLine.path = ["/opt/bin", "/usr/local/bin"]
line = Cocaine::CommandLine.new("lolwut")
line.run # => prints 'Hello', because it searches the path
Or just put it in the command:
line = Cocaine::CommandLine.new("/opt/bin/lolwut")
line.command # => "/opt/bin/lolwut"
You can see what's getting run. The 'Command' part it logs is in green for visibility!
line = Cocaine::CommandLine.new("echo", ":var", :var => "LOL!", :logger => Logger.new(STDOUT))
line.run # => Logs this with #info -> Command :: echo 'LOL!'
Or log every command:
Cocaine::CommandLine.logger = Logger.new(STDOUT)
Cocaine::CommandLine.new("date").run # => Logs this -> Command :: date
You can potentially increase performance by installing the posix-spawn
gem. This gem can keep your
application's heap from being copied when forking command line
processes. For applications with large heaps the gain can be
significant. To include posix-spawn
, simply add it to your Gemfile
or,
if you don't use bundler, install the gem.
Cocaine will attempt to choose from among 3 different ways of running commands.
The simplest is using backticks, and is the default in 1.8. In Ruby 1.9, it
will attempt to use Process.spawn
. And, as mentioned above, if the
posix-spawn
gem is installed, it will attempt to use that. If for some reason
one of the .spawn
runners don't work for you, you can override them manually
by setting a new runner, like so:
Cocaine::CommandLine.runner = Cocaine::BackticksRunner.new
And if you really want to, you can define your own Runner, though I can't imagine why you would.
If you get Error::ECHILD
errors and are using JRuby, there is a very good
chance that the error is actually in JRuby. This was brought to our attention
in #24 and probably fixed in
http://jira.codehaus.org/browse/JRUBY-6162. You will want to use the
BackticksRunner
if you are unable to update JRuby.
Security concerns must be privately emailed to security@thoughtbot.com.
Question? Idea? Problem? Bug? Comment? Concern? Like using question marks?
Copyright 2011-2012 Jon Yurek and thoughtbot, inc. This is free software, and may be redistributed under the terms specified in the LICENSE file.