# command-exec -- execute shell commands with ease
[![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/maxmeyer/command_exec)
[![Build Status](https://secure.travis-ci.org/maxmeyer/command_exec.png)](http://travis-ci.org/maxmeyer/command_exec)
## Introduction
### Description
The name of the library is `command_exec`. It's helps you running programs and
check if the run was successful. It supports a vast amount of options you find
in the one of the following sections [usage](#usage).
`Example`:
```ruby
require 'command_exec'
# command has to be in $PATH
command = CommandExec::Command.new( :echo , :parameter => 'hello world' )
command.run
p command.result
```
### Target "Group"
If you need a library to execute programs which do a job and then terminate,
`command_exec` is your friend.
If you need a library which supports error detection based on STDOUT, STDERR,
RETURN CODE and/or LOG FILE `command_exec` is the right choice.
### Limitations
The programs should NOT produce gigabytes of output (STDOUT, STDERR, LOG FILE)
to search for errors.
### Structure of documentation
Section |
Description |
Introduction |
Metainformation |
Usage |
How to use the library |
Options |
Which options are available to parametrize the library |
HowTo |
How to do ... with library |
Further reading |
Other helpful information |
## Usage
### Gem versioning
This gem uses semantic versioning. The major version is increased when breaking
changes has been made. The minor version is increased if backward-compatiable
changes introduce new functionality. The patch version is increased if a bug
was fixed and the change is backward-compatible. Please see http://semver.org/
for more information.
### Ruby Version
This gem supports ruby up from 1.9.3.
### Install gem
Install the `command_exec`-gem via `rubygems` or whatever package manager (e.g. `bundler`) you like
to use.
```bash
gem install command_exec
```
### Include library
To include the library in your code, you could use this code snippet.
```ruby
require 'command_exec'
```
### Run command
There are two forms to execute a program. You could either use the long or the
short form. In both cases a `CommandExec::Command`-object will be returned.
```ruby
command = CommandExec::Command.new( :echo , :parameter => 'hello world' )
command.run
p command.result
```
```ruby
command = CommandExec::Command.execute( :echo , :parameter => 'hello world' )
p command.result
```
### Result of command execution
That `result`-object can be used to inspect the result of the command execution. It
supports several different methods, but only some are from interest for
external use. If you want a full list, please see the API-documentation at
[rdoc.info](http://www.rdoc.info/github/maxmeyer/command_exec/CommandExec/Process).
```ruby
result = command.result
# which executable was run
result.executable
# !!content!! of log file
result.log_file
# pid unter which the command was run
result.pid
#if failed, why
result.reason_for_failure
#return code of command
result.return_code
#status of command execution
result.status
#content of stderr
result.stderr
#content of stdout
result.stdout
```
### Serialize result of command execution
There are some methods which need a little more explanation. Those methods
return a string representation of the result.
```ruby
#return an array of lines
result.to_a
#return a hash
result.to_h
#serialize data to json
result.to_json
#serialize data to string
result.to_s
#serialize data to xml
result.to_xml
#serialize data to yaml
result.to_yaml
```
One can tell those methods which data should be returned. There are different
fields available:
Field |
Symbol |
Status |
:status |
Return code |
:return_code |
STDERR |
:stderr |
STDOUT |
:stdout |
Log file |
:log_file |
Process identitfier (PID) |
:pid |
Reason for failure |
reason_for_failure |
Now, some small examples:
```ruby
#result.(field1,field2, ... , fieldn)
#result.([field1,field2, ... , fieldn])
#all fields
result.to_a
#stderr and stdout only
result.to_a(:stderr, :stdout)
#stderr and stdout only (parameters given as a single array)
result.to_a([:stderr, :stdout])
```
## Extended usage
There are multiple ways to tell `command_exec` about a command:
### Search command in PATH
If the first parameter of `run` and `execute` is a `Symbol` the library will
search for the command in the paths given in the $PATH-shell-variable.
```ruby
command = CommandExec::Command.execute( :echo ,
:parameter => 'hello world',
)
p command.result
```
### Path to command
If you prefer to use a full qualified path, this is possible as well.
```ruby
command = CommandExec::Command.execute( '/bin/echo' ,
:parameter => 'hello world',
)
p command.result
```
It also supports relative paths. But be aware to tell the library the correct
one. The base path for relative ones is the working directory of the *library*,
not the working directory of the command (see section "[Working
directory](#working_directory)" about that).
```ruby
Dir.chdir('/tmp') do
command = CommandExec::Command.execute( '../bin/echo' ,
:parameter => 'hello world',
:logger => Logger.new($stderr)
)
p command.result
end
```
## Options
### Logging
`command_exec` makes use of the Ruby `Logger`-class. If you would like to use
another class/gem, nevermind, but it has to be compatible with the `Logger`-API.
To make it easier for you, `command_exec` provides a `:logger` option. It
defaults to `Logger.new($stderr)`.
```ruby
command = CommandExec::Command.execute( :echo ,
:parameter => 'hello world',
:logger => Logger.new($stderr),
)
p command.result
```
If you prefer more or less information you can make use of the
`:lib_log_level`-option. With one exception, those log levels are the same like in
the `Logger`-class. Additionally you can use `:silent` to suppress all output
of the library, if you use the `open3` and not the `system` runner. If you
choose to use the system runner, STDOUT from the command won't be captured.
Option value |
Logger loglevel |
:debug |
Logger::DEBUG |
:info |
Logger::INFO |
:warn |
Logger::WARN |
:error |
Logger::ERROR |
:fatal |
Logger::FATAL |
:unknown |
Logger::UNKNOWN |
:silent |
no output (log device is set to nil) |
```ruby
command = CommandExec::Command.execute( :echo ,
:parameter => 'hello world' ,
:lib_log_level => :debug,
)
p command.result
```
### Command options and parameter
The next two options (command options and command parameters) are very similar.
Both will be used to build the command which should be executed. The main
difference is the position of given string in the command string.
```
```
So, if you don't want to use the `options`- and/or the `parameter`-option, you
don't need to do it. But may be there are situations, where you would like to
be as concise as possible.
Recommended:
```ruby
command = CommandExec::Command.execute( :ls ,
:options => '-al',
:parameter => '/bin',
)
p command.result
```
But also valid:
```ruby
command = CommandExec::Command.execute( :ls ,
:options => '-al /bin',
)
p command.result
```
Or:
```ruby
command = CommandExec::Command.execute( :ls ,
:parameter => '-al /bin',
)
p command.result
```
Please check if you use single or double quotes correctly! `command_exec` takes
the parameters and options as given. That's why
```ruby
#will succeed
#see debug output for reason
command = CommandExec::Command.execute( :echo ,
:options => '-e',
:parameter => "\"Thats a string\n with a newline\"",
:lib_log_level => :debug,
)
p command.result
```
isn't the same like
```ruby
#will fail
#see debug output for reason
command = CommandExec::Command.execute( :echo ,
:options => '-e',
:parameter => "Thats a string\n with a newline",
:lib_log_level => :debug,
)
p command.result
```
### Command log file
If the command creates a log file, you can tell `command_exec` about that file
via the `:log_file`-option. Honestly, this option only makes sense if you
configure `command_exec`to search for errors in the file (please see the
chapter about [Error detection](#error_detection) for further information about
that).
```ruby
command = CommandExec::Command.execute( :ls ,
:options => '-al',
:log_file => '/path/to/log_file',
)
p command.result
```
### Command search path
If you need to change the paths where a command can be found, you could use the
`:search_path`-option. It defaults to those paths found in $PATH.
It supports multiple values as `Array`:
```ruby
command = CommandExec::Command.execute( :ls ,
:options => '-al',
:search_paths => [ '/bin' ],
)
p command.result
```
Or single values as `String`:
```ruby
command = CommandExec::Command.execute( :ls ,
:options => '-al',
:search_paths => '/bin',
)
p command.result
```
### Error detection
`command_exec` is capable of searching for errors. To enable error detection
you need to activate it via the `:error_detection_on`-option. It supports error
detection on:
Search in... |
Symbol |
Return code |
:return_code |
STDOUT |
:stdout |
STDERR |
:stderr |
Log file |
:log_file |
But you need to provide information, what item indicates an error.
Indicator for... |
Options |
Type |
Return code |
:allowed_return_code
:forbidden_return_code
|
Array
|
STDERR |
:allowed_words_in_stderr
:forbidden_words_in_stderr
|
Array
|
STDOUT |
:allowed_words_in_stdout
:forbidden_words_in_stdout
|
Array
|
Log file |
:allowed_words_in_log_file
:forbidden_words_in_log_file
|
Array
|
*Return code*
If the command returns helpful return codes, those can be used to check if an
error occured. You can tell `command_exec` about allowed or forbidden return
codes.
```ruby
#All error codes except `0` will be detected as an error.
command = CommandExec::Command.execute( :false ,
:error_detection_on => [:return_code],
:error_indicators => {
:allowed_return_code => [0],
},
)
p command.result
#If the command exits with a return code of `1`, this will be detected as an
#error.
command = CommandExec::Command.execute( :false ,
:error_detection_on => [:return_code],
:error_indicators => {
:forbidden_return_code => [1],
},
)
p command.result
```
In the case of the detection of errors `command_exec`defaults to:
```ruby
:error_detection_on => [:return_code],
:allowed_return_code => [0],
```
*STDOUT*
`command_exec` can search for errors in STDOUT. To enable this functionality,
you need to set the `:error_detection_on`-option on ':stdout'. Furthermore you
need to tell the library, what strings are error indicators
(`forbidden_words_in_stdout`). If there are some strings which contain the
error string(s), but are no errors, you need to use the
`allowed_words_in_stdout`-option. The same is true, if the allowed word is in
the same line.
```ruby
#Simple error search
#will fail
command = CommandExec::Command.execute( :echo ,
:options => '-e',
:parameter => "\"wow, a test. That's great.\nBut an error occured in this line\"",
:error_detection_on => [:stdout],
:error_indicators => {
:forbidden_words_in_stdout => %w{ error }
},
)
p command.result
#error indicator in string, which is no error
#will succeed
command = CommandExec::Command.execute( :echo ,
:options => '-e',
:parameter => "\"wow, a test. That's great.\nBut no error occured in this line\"",
:error_detection_on => [:stdout],
:error_indicators => {
:forbidden_words_in_stdout => %w{ error },
:allowed_words_in_stdout => ["no error occured"] ,
},
)
p command.result
#error indicator in same line, which is no error
#will succeed
command = CommandExec::Command.execute( :echo ,
:options => '-e',
:parameter => "\"wow, a test. That's great.\nBut no error occured in this line because of some other string\"",
:error_detection_on => [:stdout],
:error_indicators => {
:forbidden_words_in_stdout => %w{ error },
:allowed_words_in_stdout => ["some other string"] ,
},
)
p command.result
```
*STDERR*
The same is true for STDERR. You need to activate the error detection via
`:error_detection_on => [:stderr]`. The error indicators can be given via
`:forbidden_words_in_stderr => %w{ error }` and `:allowed_words_in_stdout =>
["some other string"]`.
```ruby
#will fail
command = CommandExec::Command.execute( :echo ,
:options => '-e',
:parameter => "\"wow, a test. That's great.\nBut an error occured in this line\" >&2",
:error_detection_on => [:stderr],
:error_indicators => {
:forbidden_words_in_stderr => %w{ error },
},
)
p command.result
```
*LOG FILE*
To search for errors in the log file a command created during execution, you
need to provide the information where `command_exec` finds the log file (see
section [Command Log file](#log_file)).
The options are very similar to those for STDERR and STDOUT: To activate error
detection for log files use `:error_detection_on => [:log_file]`. The error
indicators can be given via `:forbidden_words_in_log_file => %w{ error }` and
`:allowed_words_in_log_file => ["some other string"]`.
```ruby
File.open('/tmp/test.log', 'w') do |f|
f.write "wow, a test. That's great.\nBut an error occured in this line"
end
#will fail
command = CommandExec::Command.execute( :echo ,
:error_detection_on => [:log_file],
:log_file => '/tmp/test.log',
:error_indicators => {
:forbidden_words_in_log_file => %w{ error },
},
)
p command.result
```
### Working directory
To change the working directory for the command you can use the `:working_directory`-option.
```ruby
command = CommandExec::Command.execute( :ls ,
:options => '-al',
:working_directory => '/tmp',
)
p command.result
```
### Error reaction
If an error occured, `command_exec` can raise an exception, 'throw' an error or
do nothing at all. Besides the configured option, on every run it returns the
result for the run (see [Result of command
execution](#result_of_command_execution) for more details).
*Raise an exception aka error*
If an error occured during command execution, you can tell `command_exec` to
raise an exception.
```ruby
begin
command = CommandExec::Command.execute( :false ,
:on_error_do => :raise_error,
)
rescue CommandExec::Exceptions::CommandExecutionFailed => e
puts e.message
end
```
*Throw error*
If you prefer not to use execptions, you can use ruby's
`throw`-`catch`-mechanism.
```ruby
catch :command_execution_failed do
command = CommandExec::Command.execute( :false ,
:on_error_do => :throw_error,
)
end
```
### Runner
Today there are two runners available: `:open3` and `system`. Use the first one
if you want `:stdout` and `:stderr` to be captured and searched for errors. If
you're only interested in the `:return_code` you could use the
`:system`-runner. Please be aware, that using the `system`-runner + error
detection on `stdout`, `stderr` is not working as you might expect.
```ruby
#will fail
command = CommandExec::Command.execute( :echo ,
:options => '-e',
:parameter => "\"wow, a test. That's great.\nBut an error occured in this line\"",
:error_detection_on => [:stdout],
:error_indicators => {
:forbidden_words_in_stdout => %w{ error }
},
:run_via => :open3,
)
p command.result
#will succeed, because stdout was not caputured
command = CommandExec::Command.execute( :echo ,
:options => '-e',
:parameter => "\"wow, a test. That's great.\nBut an error occured in this line\"",
:error_detection_on => [:stdout],
:error_indicators => {
:forbidden_words_in_stdout => %w{ error }
},
:run_via => :system,
)
p command.result
```
## HowTo
TBD
## Further Reading
* API-documentation: http://rdoc.info/github/maxmeyer/command_exec/frames
## Dependencies
Please see the gemspec for runtime dependencies and the 'Gemfile' for
development dependencies.
## Todo
Please see TODO.md for enhancements which are planned for implementation.
## Development
1. Fork it
2. Create your remote (`git remote add `)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Added some feature'`)
4. Push to the branch (`git push my-new-feature`)
5. Create new Pull Request
The API-documentation can be found at
http://rdoc.info/github/maxmeyer/command_exec/frames
Please see 'http://git-scm.com/book' first if you have further questions about
`git`.
## Copyright
(c) 2012-, Max Meyer
## License
Please see LICENSE.md for license text.