tty-command 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +11 -10
- data/CHANGELOG.md +13 -0
- data/Gemfile +2 -2
- data/README.md +66 -11
- data/examples/redirect_stdin.rb +14 -0
- data/lib/tty/command.rb +13 -5
- data/lib/tty/command/cmd.rb +6 -6
- data/lib/tty/command/printers/abstract.rb +5 -5
- data/lib/tty/command/process_runner.rb +1 -0
- data/lib/tty/command/result.rb +29 -19
- data/lib/tty/command/truncator.rb +2 -2
- data/lib/tty/command/version.rb +1 -1
- data/tty-command.gemspec +1 -1
- metadata +21 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a35d801abdb3db9b76f2b73c9b602c40b67dce15
|
4
|
+
data.tar.gz: b9ba7fd35be52eca694285ef8bcf0c79b9eac01c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c49e3e656210ef586253549099048e723fcd7cc337c1f0a762e541a01e9bcfb90515ce3178cfe5a7d74e4860783ba0fb3ac52b71911141f3801ad72ef8babd0e
|
7
|
+
data.tar.gz: 8c574b87afd46f3dc22839ebacf2e7593c4d7ace915cf6954ecbaa2178c7629d5af99384eaa53af004c08405d0f3787aa2545b4b984f527983c982b3e454efa4
|
data/.travis.yml
CHANGED
@@ -4,20 +4,21 @@ sudo: false
|
|
4
4
|
cache: bundler
|
5
5
|
script: "bundle exec rake ci"
|
6
6
|
rvm:
|
7
|
-
-
|
8
|
-
- 2.
|
9
|
-
- 2.
|
10
|
-
- 2.
|
11
|
-
- 2.
|
7
|
+
- 2.0.0
|
8
|
+
- 2.1.10
|
9
|
+
- 2.2.6
|
10
|
+
- 2.3.3
|
11
|
+
- 2.4.0
|
12
12
|
- ruby-head
|
13
|
-
- jruby
|
14
|
-
- jruby-
|
15
|
-
|
13
|
+
- jruby-9.1.1.0
|
14
|
+
- jruby-head
|
15
|
+
env:
|
16
|
+
global:
|
17
|
+
- JRUBY_OPTS=''
|
16
18
|
matrix:
|
17
19
|
allow_failures:
|
18
20
|
- rvm: ruby-head
|
19
|
-
- rvm: jruby
|
20
|
-
- rvm: jruby-9000
|
21
|
+
- rvm: jruby-head
|
21
22
|
fast_finish: true
|
22
23
|
branches:
|
23
24
|
only: master
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Change log
|
2
2
|
|
3
|
+
## [v0.3.0] - 2017-01-13
|
4
|
+
|
5
|
+
### Added
|
6
|
+
* Add ability to enumerate Result output
|
7
|
+
* Add #record_saparator for specifying delimiter for enumeration
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
* Change Abstract printer to separate arguments out
|
11
|
+
* Change Cmd to prevent modifications
|
12
|
+
* Change pastel dependency version
|
13
|
+
|
3
14
|
## [v0.2.0] - 2016-07-03
|
4
15
|
|
5
16
|
### Added
|
@@ -12,4 +23,6 @@
|
|
12
23
|
|
13
24
|
* Initial implementation and release
|
14
25
|
|
26
|
+
[v0.3.0]: https://github.com/piotrmurach/tty-command/compare/v0.2.0...v0.3.0
|
27
|
+
[v0.2.0]: https://github.com/piotrmurach/tty-command/compare/v0.1.0...v0.2.0
|
15
28
|
[v0.1.0]: https://github.com/piotrmurach/tty-command/compare/v0.1.0
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -55,14 +55,16 @@ Or install it yourself as:
|
|
55
55
|
* [3.2. Options](#32-options)
|
56
56
|
* [3.2.1. Current directory](#321-current-directory)
|
57
57
|
* [3.2.2. Redirection](#322-redirection)
|
58
|
-
* [3.2.3.
|
59
|
-
* [3.2.4.
|
60
|
-
* [3.2.5.
|
61
|
-
* [3.2.6.
|
58
|
+
* [3.2.3. Handling input](#323-handling-input)
|
59
|
+
* [3.2.4. Timeout](#324-timeout)
|
60
|
+
* [3.2.5. User](#324-user)
|
61
|
+
* [3.2.6. Group](#325-group)
|
62
|
+
* [3.2.7. Umask](#326-umask)
|
62
63
|
* [3.3. Result](#33-result)
|
63
64
|
* [3.3.1. success?](#331-success)
|
64
65
|
* [3.3.2. failure?](#332-failure)
|
65
66
|
* [3.3.3. exited?](#333-exited)
|
67
|
+
* [3.3.4. each](#334-each)
|
66
68
|
* [3.4. Custom printer](#34-custom-printer)
|
67
69
|
* [4. Example](#4-example)
|
68
70
|
|
@@ -239,9 +241,15 @@ cmd.run(:echo, 'hello', chdir: '/var/tmp')
|
|
239
241
|
|
240
242
|
#### 3.2.2 Redirection
|
241
243
|
|
242
|
-
|
244
|
+
There are few ways you can redirect commands output.
|
243
245
|
|
244
|
-
You can
|
246
|
+
You can directly use shell redirection facility like so:
|
247
|
+
|
248
|
+
```ruby
|
249
|
+
cmd.run("ls 1&>2")
|
250
|
+
```
|
251
|
+
|
252
|
+
You can provide the streams as additional hash options where the key is one of `:in`, `:out`, `:err`, a fixnum(a file descriptor for the child process), an IO or array. The pair value can be a filename for redirection.
|
245
253
|
|
246
254
|
```ruby
|
247
255
|
cmd.run(:ls, :in => "/dev/null") # read mode
|
@@ -264,7 +272,22 @@ cmd.run(:ls, '-la', :stderr => :stdout)
|
|
264
272
|
cmd.run(:ls, '-la', 2 => 1)
|
265
273
|
```
|
266
274
|
|
267
|
-
#### 3.2.3
|
275
|
+
#### 3.2.3 Handling Input
|
276
|
+
|
277
|
+
You can pass input via the :in option, by passing a StringIO Object. This object might have more than one line, if the executed command reads more than once from STDIN.
|
278
|
+
|
279
|
+
Assume you have run a program, that first asks for your email address and then for a password:
|
280
|
+
|
281
|
+
```ruby
|
282
|
+
in_stream = StringIO.new
|
283
|
+
in_stream.puts "username@example.com"
|
284
|
+
in_stream.puts "password"
|
285
|
+
in_stream.rewind
|
286
|
+
|
287
|
+
TTY::Command.new.run("my_cli_program", "login", in: in_stream).out
|
288
|
+
```
|
289
|
+
|
290
|
+
#### 3.2.4 Timeout
|
268
291
|
|
269
292
|
You can timeout command execuation by providing the `:timeout` option in seconds:
|
270
293
|
|
@@ -274,7 +297,7 @@ cmd.run("while test 1; sleep 1; done", timeout: 5)
|
|
274
297
|
|
275
298
|
Please run `examples/timeout.rb` to see timeout in action.
|
276
299
|
|
277
|
-
#### 3.2.
|
300
|
+
#### 3.2.5 User
|
278
301
|
|
279
302
|
To run command as a given user do:
|
280
303
|
|
@@ -282,7 +305,7 @@ To run command as a given user do:
|
|
282
305
|
cmd.run(:echo, 'hello', user: 'piotr')
|
283
306
|
```
|
284
307
|
|
285
|
-
#### 3.2.
|
308
|
+
#### 3.2.6 Group
|
286
309
|
|
287
310
|
To run command as part of group do:
|
288
311
|
|
@@ -290,7 +313,7 @@ To run command as part of group do:
|
|
290
313
|
cmd.run(:echo, 'hello', group: 'devs')
|
291
314
|
```
|
292
315
|
|
293
|
-
#### 3.2.
|
316
|
+
#### 3.2.7 Umask
|
294
317
|
|
295
318
|
To run command with umask do:
|
296
319
|
|
@@ -343,6 +366,37 @@ result.exited? # => true
|
|
343
366
|
result.complete? # => true
|
344
367
|
```
|
345
368
|
|
369
|
+
#### 3.3.4 each
|
370
|
+
|
371
|
+
The result itself is an enumerable and allows you to iterate over the stdout output:
|
372
|
+
|
373
|
+
```ruby
|
374
|
+
result = cmd.run(:ls, '-1')
|
375
|
+
result.each { |line| puts line }
|
376
|
+
# =>
|
377
|
+
# CHANGELOG.md
|
378
|
+
# CODE_OF_CONDUCT.md
|
379
|
+
# Gemfile
|
380
|
+
# Gemfile.lock
|
381
|
+
# ...
|
382
|
+
# lib
|
383
|
+
# pkg
|
384
|
+
# spec
|
385
|
+
# tasks
|
386
|
+
```
|
387
|
+
|
388
|
+
By default the linefeed character `\n` is used as a delimiter but this can be changed either globally by calling `record_separator`:
|
389
|
+
|
390
|
+
```ruby
|
391
|
+
TTY::Command.record_separator = "\n\r"
|
392
|
+
```
|
393
|
+
|
394
|
+
or configured per `each` call by passing delimiter as an argument:
|
395
|
+
|
396
|
+
```ruby
|
397
|
+
cmd.run(:ls, '-1').each("\t") { ... }
|
398
|
+
```
|
399
|
+
|
346
400
|
### 3.4 Custom printer
|
347
401
|
|
348
402
|
If the built-in printers do not meet your requirements you can create your own. At the very minimum you need to specify the `write` method that will be called during the lifecycle of command execution:
|
@@ -359,6 +413,7 @@ printer = CustomPrinter
|
|
359
413
|
cmd = TTY::Command.new(printer: printer)
|
360
414
|
```
|
361
415
|
|
416
|
+
|
362
417
|
## 4. Example
|
363
418
|
|
364
419
|
Here's a slightly more elaborate example to illustrate how tty-command can improve on plain old shell scripts. This example installs a new version of Ruby on an Ubuntu machine.
|
@@ -398,4 +453,4 @@ The gem is available as open source under the terms of the [MIT License](http://
|
|
398
453
|
|
399
454
|
## Copyright
|
400
455
|
|
401
|
-
Copyright (c) 2016 Piotr Murach. See LICENSE for further details.
|
456
|
+
Copyright (c) 2016-2017 Piotr Murach. See LICENSE for further details.
|
data/lib/tty/command.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require 'thread'
|
4
3
|
require 'rbconfig'
|
5
4
|
require 'tty/command/version'
|
6
5
|
require 'tty/command/cmd'
|
@@ -23,6 +22,14 @@ module TTY
|
|
23
22
|
RbConfig::CONFIG['bindir'],
|
24
23
|
RbConfig::CONFIG['ruby_install_name'] + RbConfig::CONFIG['EXEEXT'])
|
25
24
|
|
25
|
+
def self.record_separator
|
26
|
+
@record_separator ||= $/
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.record_separator=(sep)
|
30
|
+
@record_separator = sep
|
31
|
+
end
|
32
|
+
|
26
33
|
attr_reader :printer
|
27
34
|
|
28
35
|
# Initialize a Command object
|
@@ -50,6 +57,11 @@ module TTY
|
|
50
57
|
# @example
|
51
58
|
# cmd.run(command, [argv1, ..., argvN], [options])
|
52
59
|
#
|
60
|
+
# @example
|
61
|
+
# cmd.run(command, ...) do |result|
|
62
|
+
# ...
|
63
|
+
# end
|
64
|
+
#
|
53
65
|
# @param [String] command
|
54
66
|
# the command to run
|
55
67
|
#
|
@@ -124,10 +136,6 @@ module TTY
|
|
124
136
|
@dry_run
|
125
137
|
end
|
126
138
|
|
127
|
-
def printer
|
128
|
-
use_printer(@printer_name, color: @color, uuid: @uuid)
|
129
|
-
end
|
130
|
-
|
131
139
|
private
|
132
140
|
|
133
141
|
# @api private
|
data/lib/tty/command/cmd.rb
CHANGED
@@ -17,6 +17,10 @@ module TTY
|
|
17
17
|
# @api public
|
18
18
|
attr_reader :options
|
19
19
|
|
20
|
+
# Unique identifier
|
21
|
+
# @api public
|
22
|
+
attr_reader :uuid
|
23
|
+
|
20
24
|
# Initialize a new Cmd object
|
21
25
|
#
|
22
26
|
# @api private
|
@@ -46,13 +50,9 @@ module TTY
|
|
46
50
|
end
|
47
51
|
@env ||= {}
|
48
52
|
@options = opts
|
49
|
-
end
|
50
53
|
|
51
|
-
|
52
|
-
|
53
|
-
# @api public
|
54
|
-
def uuid
|
55
|
-
@uuid ||= SecureRandom.uuid.split('-')[0]
|
54
|
+
@uuid = SecureRandom.uuid.split('-')[0]
|
55
|
+
freeze
|
56
56
|
end
|
57
57
|
|
58
58
|
# The shell environment variables
|
@@ -21,8 +21,8 @@ module TTY
|
|
21
21
|
def initialize(output, options = {})
|
22
22
|
@output = output
|
23
23
|
@options = options
|
24
|
-
enabled = options.fetch(:color) { true }
|
25
|
-
@color = ::Pastel.new(output: output, enabled: enabled)
|
24
|
+
@enabled = options.fetch(:color) { true }
|
25
|
+
@color = ::Pastel.new(output: output, enabled: @enabled)
|
26
26
|
end
|
27
27
|
|
28
28
|
def print_command_start(cmd, *args)
|
@@ -30,15 +30,15 @@ module TTY
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def print_command_out_data(cmd, *args)
|
33
|
-
write(args.join)
|
33
|
+
write(args.join(' '))
|
34
34
|
end
|
35
35
|
|
36
36
|
def print_command_err_data(cmd, *args)
|
37
|
-
write(args.join)
|
37
|
+
write(args.join(' '))
|
38
38
|
end
|
39
39
|
|
40
40
|
def print_command_exit(cmd, *args)
|
41
|
-
write(args.join)
|
41
|
+
write(args.join(' '))
|
42
42
|
end
|
43
43
|
|
44
44
|
def write(message)
|
data/lib/tty/command/result.rb
CHANGED
@@ -6,23 +6,37 @@ module TTY
|
|
6
6
|
#
|
7
7
|
# @api public
|
8
8
|
class Result
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
# All data written out to process's stdout stream
|
12
|
+
attr_reader :out
|
13
|
+
alias stdout out
|
14
|
+
|
15
|
+
# All data written out to process's stdin stream
|
16
|
+
attr_reader :err
|
17
|
+
alias stderr err
|
18
|
+
|
9
19
|
def initialize(status, out, err)
|
10
20
|
@status = status
|
11
21
|
@out = out
|
12
22
|
@err = err
|
13
23
|
end
|
14
24
|
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
@
|
25
|
+
# Enumerate over output lines
|
26
|
+
#
|
27
|
+
# @param [String] separator
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def each(separator = nil)
|
31
|
+
sep = separator || TTY::Command.record_separator
|
32
|
+
return unless @out
|
33
|
+
elements = @out.split(sep)
|
34
|
+
if block_given?
|
35
|
+
elements.each { |line| yield(line) }
|
36
|
+
else
|
37
|
+
elements.to_enum
|
38
|
+
end
|
24
39
|
end
|
25
|
-
alias_method :stderr, :err
|
26
40
|
|
27
41
|
# Information on how the process exited
|
28
42
|
#
|
@@ -30,8 +44,8 @@ module TTY
|
|
30
44
|
def exit_status
|
31
45
|
@status
|
32
46
|
end
|
33
|
-
|
34
|
-
|
47
|
+
alias exitstatus exit_status
|
48
|
+
alias status exit_status
|
35
49
|
|
36
50
|
def to_i
|
37
51
|
@status
|
@@ -48,20 +62,16 @@ module TTY
|
|
48
62
|
def exited?
|
49
63
|
@status != nil
|
50
64
|
end
|
51
|
-
|
65
|
+
alias complete? exited?
|
52
66
|
|
53
67
|
def success?
|
54
|
-
|
55
|
-
@status == 0
|
56
|
-
else
|
57
|
-
false
|
58
|
-
end
|
68
|
+
exited? ? @status.zero? : false
|
59
69
|
end
|
60
70
|
|
61
71
|
def failure?
|
62
72
|
!success?
|
63
73
|
end
|
64
|
-
|
74
|
+
alias failed? failure?
|
65
75
|
|
66
76
|
def ==(other)
|
67
77
|
return false unless other.is_a?(TTY::Command::Result)
|
@@ -47,7 +47,7 @@ module TTY
|
|
47
47
|
content = copy(content, @suffix)
|
48
48
|
end
|
49
49
|
end
|
50
|
-
|
50
|
+
alias << write
|
51
51
|
|
52
52
|
# Truncated representation of the content
|
53
53
|
#
|
@@ -67,7 +67,7 @@ module TTY
|
|
67
67
|
res << @suffix
|
68
68
|
res
|
69
69
|
end
|
70
|
-
|
70
|
+
alias to_s read
|
71
71
|
|
72
72
|
private
|
73
73
|
|
data/lib/tty/command/version.rb
CHANGED
data/tty-command.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.add_dependency 'pastel', '~> 0.
|
22
|
+
spec.add_dependency 'pastel', '~> 0.7.0'
|
23
23
|
|
24
24
|
spec.add_development_dependency 'bundler', '>= 1.5.0', '< 2.0'
|
25
25
|
spec.add_development_dependency 'rake', '~> 10.0'
|
metadata
CHANGED
@@ -1,75 +1,75 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tty-command
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Murach
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pastel
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.7.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.7.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: 1.5.0
|
34
|
-
- - <
|
34
|
+
- - "<"
|
35
35
|
- !ruby/object:Gem::Version
|
36
36
|
version: '2.0'
|
37
37
|
type: :development
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
|
-
- -
|
41
|
+
- - ">="
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: 1.5.0
|
44
|
-
- - <
|
44
|
+
- - "<"
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '2.0'
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rake
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
|
-
- - ~>
|
51
|
+
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '10.0'
|
54
54
|
type: :development
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
|
-
- - ~>
|
58
|
+
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: '10.0'
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
62
|
name: rspec
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
65
|
-
- - ~>
|
65
|
+
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
67
|
version: '3.0'
|
68
68
|
type: :development
|
69
69
|
prerelease: false
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
|
-
- - ~>
|
72
|
+
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: '3.0'
|
75
75
|
description: Execute shell commands with pretty output logging and capture their stdout,
|
@@ -81,9 +81,9 @@ executables: []
|
|
81
81
|
extensions: []
|
82
82
|
extra_rdoc_files: []
|
83
83
|
files:
|
84
|
-
- .gitignore
|
85
|
-
- .rspec
|
86
|
-
- .travis.yml
|
84
|
+
- ".gitignore"
|
85
|
+
- ".rspec"
|
86
|
+
- ".travis.yml"
|
87
87
|
- CHANGELOG.md
|
88
88
|
- CODE_OF_CONDUCT.md
|
89
89
|
- Gemfile
|
@@ -97,6 +97,7 @@ files:
|
|
97
97
|
- examples/env.rb
|
98
98
|
- examples/logger.rb
|
99
99
|
- examples/redirect_stderr.rb
|
100
|
+
- examples/redirect_stdin.rb
|
100
101
|
- examples/redirect_stdout.rb
|
101
102
|
- examples/timeout.rb
|
102
103
|
- lib/tty-command.rb
|
@@ -128,17 +129,17 @@ require_paths:
|
|
128
129
|
- lib
|
129
130
|
required_ruby_version: !ruby/object:Gem::Requirement
|
130
131
|
requirements:
|
131
|
-
- -
|
132
|
+
- - ">="
|
132
133
|
- !ruby/object:Gem::Version
|
133
134
|
version: '0'
|
134
135
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
136
|
requirements:
|
136
|
-
- -
|
137
|
+
- - ">="
|
137
138
|
- !ruby/object:Gem::Version
|
138
139
|
version: '0'
|
139
140
|
requirements: []
|
140
141
|
rubyforge_project:
|
141
|
-
rubygems_version: 2.
|
142
|
+
rubygems_version: 2.5.1
|
142
143
|
signing_key:
|
143
144
|
specification_version: 4
|
144
145
|
summary: Execute shell commands with pretty output logging and capture their stdout,
|