command-exec -- execute shell commands with ease
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.
Example
:
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 |
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.
gem install command_exec
Include library
To include the library in your code, you could use this code snippet.
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.
command = CommandExec::Command.new( :echo , :parameter => 'hello world' )
command.run
p command.result
command = CommandExec::Command.execute( :echo , :parameter => 'hello world' )
p command.result
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.
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.
#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:
#result.<method>(field1,field2, ... , fieldn)
#result.<method>([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.
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.
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" about that).
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)
.
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) |
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.
<command> <options> <parameter>
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:
command = CommandExec::Command.execute( :ls ,
:options => '-al',
:parameter => '/bin',
)
p command.result
But also valid:
command = CommandExec::Command.execute( :ls ,
:options => '-al /bin',
)
p command.result
Or:
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
#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
#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
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 for further information about
that).
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
:
command = CommandExec::Command.execute( :ls ,
:options => '-al',
:search_paths => [ '/bin' ],
)
p command.result
Or single values as String
:
command = CommandExec::Command.execute( :ls ,
:options => '-al',
:search_paths => '/bin',
)
p command.result
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.
#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:
: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.
#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"]
.
#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).
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"]
.
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
To change the working directory for the command you can use the :working_directory
-option.
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 for more details).
Raise an exception aka error
If an error occured during command execution, you can tell command_exec
to
raise an exception.
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.
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.
#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
- Fork it
- Create your remote (
git remote add <your_remote_repo> <path_to_repo>
) - Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Added some feature'
) - Push to the branch (
git push <your_remote_repo> my-new-feature
) - 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.