eksek 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gemspec +18 -0
- data/README.md +84 -0
- data/lib/eksek.rb +18 -0
- data/lib/eksek_error.rb +6 -0
- data/lib/eksek_result.rb +33 -0
- data/lib/eksekuter.rb +80 -0
- data/license.txt +15 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 38c99ca643c947821f164f389981a9958424ede39ba1aeea5ba956265291d0ff
|
4
|
+
data.tar.gz: bc09955bf615924204cad88a4a7b840826633184b6830207cb1193e71f132346
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a91f53242d38998a7b2ab739050379b07108697c488f9dc69120e8538c6ee1299a66cc07ea203bca54aa76341d8c817f107af365cbaa669f6c365704ca780757
|
7
|
+
data.tar.gz: 2918aa0f4ecea9affba5a6d531a7fa7cfd89ae2186ae30f033efda40e9480a91157afe4d20879591e7e8013a92a19adea79284dcc8cbc966737d6b6624080f7f
|
data/.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'eksek'
|
3
|
+
s.version = '0.1.0'
|
4
|
+
s.authors = ['Johnny Lee-Othon', 'Thomas Bretzke']
|
5
|
+
s.homepage = 'https://github.com/taccon/eksek'
|
6
|
+
s.summary = 'A better backticks'
|
7
|
+
s.description = <<~END
|
8
|
+
Execute shell commands and easily get stdout, stderr, exit code, and more
|
9
|
+
END
|
10
|
+
s.license = 'ISC'
|
11
|
+
|
12
|
+
s.files = Dir['.gemspec', 'lib/**/*.rb', 'license.txt', 'readme.md']
|
13
|
+
|
14
|
+
s.required_ruby_version = '~> 2.3'
|
15
|
+
s.add_development_dependency 'rspec','~> 3'
|
16
|
+
s.add_development_dependency 'rubocop', '~> 0'
|
17
|
+
s.add_development_dependency 'bundler', '~> 1.0'
|
18
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# eksek
|
2
|
+
|
3
|
+
[![Travis](https://img.shields.io/travis/jleeothon/eksek.svg)](https://travis-ci.org/jleeothon/eksek)
|
4
|
+
|
5
|
+
`eksek` is a library to run shell commands synchronously and obtain any of the standard output, standard error, and exit code with flexibility.
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
### Basic usage
|
10
|
+
|
11
|
+
Use the `eksek` to execute a command:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
eksek 'echo Hello'
|
15
|
+
```
|
16
|
+
|
17
|
+
This returns an `EksekResult` object providing the following methods:
|
18
|
+
|
19
|
+
- `exit` returns the exit code.
|
20
|
+
- `stdout` returns the standard output as a string.
|
21
|
+
- `stderr` returns the standard error as a string.
|
22
|
+
- `success?` returns `true` or `false` depending of the exit code (`0` for `true`).
|
23
|
+
- `success!` throws an exception if the command exited with a non-0 code.
|
24
|
+
|
25
|
+
The `success!` method can be chained with any other of the above ones and it is wrapped in the convenience method `eksek!` to have a "fail or return" like so:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
puts eksek!('echo Hello').stdout # Hello
|
29
|
+
|
30
|
+
# The above is essentially the same as:
|
31
|
+
|
32
|
+
puts eksek('echo Hello').success!.stdout
|
33
|
+
```
|
34
|
+
|
35
|
+
### Writing to stdin
|
36
|
+
|
37
|
+
To write into the standard input a block can be used:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
r = eksek('read A; echo $A') { |stdin| stdin.write "Hello" }
|
41
|
+
r.success!
|
42
|
+
```
|
43
|
+
|
44
|
+
If the block returns a `String` or `IO`, it will be written into the stdio.
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
r = eksek('read A; echo $A') { "Hello" }
|
48
|
+
r.success!
|
49
|
+
|
50
|
+
r = eksek('read A; echo $A') { File.open('myfile.txt') }
|
51
|
+
r.success!
|
52
|
+
```
|
53
|
+
|
54
|
+
### Passing other options
|
55
|
+
|
56
|
+
`eksek` has the same signature as `Process#spawn`. This means that:
|
57
|
+
|
58
|
+
- the first parameter can optionally be a hash, passed as the process environment;
|
59
|
+
- the command can be passed as a single string or as variable-length arguments;
|
60
|
+
- other options can be passed as hash arguments.
|
61
|
+
|
62
|
+
Additionally, the environment will have its keys stringified, so that symbols can be used too. For example:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
r = eksek { A: 'Hello' }, 'echo $A'
|
66
|
+
puts r.stdout # Hello
|
67
|
+
|
68
|
+
r = eksek 'echo', 'Hello'
|
69
|
+
puts r.stdout # Hello
|
70
|
+
|
71
|
+
r = eksek 'echo $PWD', chdir: '/tmp'
|
72
|
+
puts r.stdout # /tmp
|
73
|
+
```
|
74
|
+
|
75
|
+
### Further information
|
76
|
+
|
77
|
+
In case you prefer an object oriented method, you can also use the `Eksekuter` class that is used by the `eksek` method directly. The following examples are basically the same:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
# With eksek
|
81
|
+
eksek 'echo Hello' { 'This goes to stdin.' }
|
82
|
+
# With Eksekuter
|
83
|
+
Eksekuter.new('echo Hello').run { 'This goes to stdin.' }
|
84
|
+
```
|
data/lib/eksek.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'eksekuter'
|
4
|
+
|
5
|
+
# Executes shell commands and can be specified to return the standard output,
|
6
|
+
# standard error, etc.
|
7
|
+
# Accepts the same options as Process.spawn e.g. :chdir, :env.
|
8
|
+
module Kernel
|
9
|
+
private
|
10
|
+
|
11
|
+
def eksek *args, **opts, &block
|
12
|
+
Eksekuter.new(*args, **opts).run(&block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def eksek! *args, **opts, &block
|
16
|
+
eksek(*args, **opts, &block).success!
|
17
|
+
end
|
18
|
+
end
|
data/lib/eksek_error.rb
ADDED
data/lib/eksek_result.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'eksek_error'
|
4
|
+
|
5
|
+
# Describes a result object to be used for evaluating
|
6
|
+
# return values from a command
|
7
|
+
class EksekResult
|
8
|
+
# rubocop:disable Metrics/ParameterLists
|
9
|
+
def initialize env, cmd, exit_code, success, stdout, stderr
|
10
|
+
@env = env
|
11
|
+
@cmd = cmd
|
12
|
+
@exit_code = exit_code
|
13
|
+
@success = success
|
14
|
+
@stdout = stdout
|
15
|
+
@stderr = stderr
|
16
|
+
end
|
17
|
+
# rubocop:enable Metrics/ParameterLists
|
18
|
+
|
19
|
+
attr_reader :exit_code, :stdout, :stderr
|
20
|
+
|
21
|
+
def success?
|
22
|
+
@success
|
23
|
+
end
|
24
|
+
|
25
|
+
def success!
|
26
|
+
raise EksekError, "Command failed: #{@cmd.inspect}" unless success?
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
stdout
|
32
|
+
end
|
33
|
+
end
|
data/lib/eksekuter.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
|
5
|
+
require_relative 'eksek_result'
|
6
|
+
|
7
|
+
# Class that command execution is delegated to by Eksek.
|
8
|
+
class Eksekuter
|
9
|
+
def initialize *args, **opts
|
10
|
+
@env = args[0].is_a?(Hash) ? args.shift : {}
|
11
|
+
@env = @env.each_with_object({}) { |(k, v), o| o[k.to_s] = v }
|
12
|
+
@cmd = args.size == 1 ? args.first : args
|
13
|
+
@opts = opts
|
14
|
+
end
|
15
|
+
|
16
|
+
def run &block
|
17
|
+
spawn_process
|
18
|
+
write_and_close_stdin(&block)
|
19
|
+
wait
|
20
|
+
read_and_close_stdout_stderr
|
21
|
+
assemble_result
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader(
|
27
|
+
:cmd,
|
28
|
+
:env,
|
29
|
+
:err_str,
|
30
|
+
:opts,
|
31
|
+
:out_str,
|
32
|
+
:process_status,
|
33
|
+
:stdin,
|
34
|
+
:wait_thr,
|
35
|
+
)
|
36
|
+
|
37
|
+
def stdout
|
38
|
+
@stdout ||= StringIO.new('', 'r')
|
39
|
+
end
|
40
|
+
|
41
|
+
def stderr
|
42
|
+
@stderr ||= StringIO.new('', 'r')
|
43
|
+
end
|
44
|
+
|
45
|
+
def spawn_process
|
46
|
+
@stdin, @stdout, @stderr, @wait_thr = Open3.popen3(env, *cmd, opts)
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def write_and_close_stdin
|
51
|
+
return if stdin.closed? || !block_given?
|
52
|
+
block_result = yield stdin
|
53
|
+
block_result = StringIO.new(block_result) if block_result.is_a? String
|
54
|
+
IO.copy_stream(block_result, stdin) if block_result.respond_to? :read
|
55
|
+
stdin.close
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def wait
|
60
|
+
@process_status = wait_thr.value
|
61
|
+
end
|
62
|
+
|
63
|
+
def read_and_close_stdout_stderr
|
64
|
+
streams = [stdout, stderr]
|
65
|
+
@out_str, @err_str = streams.map(&:read).map(&:chomp)
|
66
|
+
streams.each(&:close)
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def assemble_result
|
71
|
+
EksekResult.new(
|
72
|
+
env,
|
73
|
+
cmd,
|
74
|
+
process_status.exitstatus,
|
75
|
+
process_status.success?,
|
76
|
+
out_str,
|
77
|
+
err_str,
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
data/license.txt
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
ISC License
|
2
|
+
|
3
|
+
Copyright (c) 2018, Johnny Enrique Lee-Othon
|
4
|
+
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: eksek
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Johnny Lee-Othon
|
8
|
+
- Thomas Bretzke
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2018-04-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '3'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '3'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rubocop
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: bundler
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '1.0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '1.0'
|
56
|
+
description: 'Execute shell commands and easily get stdout, stderr, exit code, and
|
57
|
+
more
|
58
|
+
|
59
|
+
'
|
60
|
+
email:
|
61
|
+
executables: []
|
62
|
+
extensions: []
|
63
|
+
extra_rdoc_files: []
|
64
|
+
files:
|
65
|
+
- ".gemspec"
|
66
|
+
- README.md
|
67
|
+
- lib/eksek.rb
|
68
|
+
- lib/eksek_error.rb
|
69
|
+
- lib/eksek_result.rb
|
70
|
+
- lib/eksekuter.rb
|
71
|
+
- license.txt
|
72
|
+
homepage: https://github.com/taccon/eksek
|
73
|
+
licenses:
|
74
|
+
- ISC
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - "~>"
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '2.3'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.7.6
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: A better backticks
|
96
|
+
test_files: []
|