Cocaine - 用于在Ruby代码中执行命令行的库
Cocaine - 用于在Ruby代码中执行命令行的库
Ruby CLI工具
共796Star
详细介绍
Cocaine
A small library for doing (command) lines.
Usage
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")
line.command(in: "omg.jpg",
resolution: "32x32",
out: "omg_thumb.jpg")
# => "convert 'omg.jpg' -scale '32x32' 'omg_thumb.jpg'"
It prevents attempts at being bad:
line = Cocaine::CommandLine.new("cat", ":file")
line.command(file: "haha`rm -rf /`.txt") # => "cat 'haha`rm -rf /`.txt'"
line = Cocaine::CommandLine.new("cat", ":file")
line.command(file: "ohyeah?'`rm -rf /`.ha!") # => "cat 'ohyeah?'\\''`rm -rf /`.ha!'"
NOTE: It only does that for arguments interpolated via run
, NOT arguments passed into new
(see 'Security' below):
line = Cocaine::CommandLine.new("echo", "haha`whoami`")
line.command # => "echo haha`whoami`"
line.run # => "hahawebserver"
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", logger: Logger.new(STDOUT))
line.run(var: "LOL!") # => 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
Security
Short version: Only pass user-generated data into the run
method and NOT new
.
As shown in examples above, Cocaine will only shell-escape what is passed in as interpolations to the run
method. It WILL NOT escape what is passed in to the second argument of new
. Cocaine assumes that you will not be manually passing user-generated data to that argument and will be using it as a template for your command line's structure.
POSIX Spawn
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.
Runners
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::CommandLine::BackticksRunner.new
And if you really want to, you can define your own Runner, though I can't imagine why you would.
JRuby issues
Caveat
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 https://github.com/thoughtbot/cocaine/issues/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.
Spawn warning
If you get unsupported spawn option: out
warning (like in issue 38), try to use PopenRunner
:
Cocaine::CommandLine.runner = Cocaine::CommandLine::PopenRunner.new
Thread Safety
Cocaine should be thread safe. As discussed here, in this climate_control thread, climate_control, which modifies the environment under which commands are run for the BackticksRunner and PopenRunner, is thread-safe but not reentrant. Please let us know if you find this is ever not the case.
Feedback
Security concerns must be privately emailed to security@thoughtbot.com.
Question? Idea? Problem? Bug? Comment? Concern? Like using question marks?
Credits
Thank you to all the contributors!
Cocaine is maintained and funded by thoughtbot, inc
The names and logos for thoughtbot are trademarks of thoughtbot, inc.
License
Copyright 2011-2014 Jon Yurek and thoughtbot, inc. This is free software, and may be redistributed under the terms specified in the LICENSE file.
-
0 Star
-
492 Star
-
261 Star
-
363 Star
-
365 Star
-
1552 Star
-
0 Star
-
72 Star
-
732 Star
-
0 Star