sys_cmd 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +3 -1
- data/README.md +67 -1
- data/lib/sys_cmd.rb +65 -12
- data/lib/sys_cmd/version.rb +1 -1
- data/sys_cmd.gemspec +2 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1699ccea4cb7b0bc8248c247383683ba89dfa43a
|
4
|
+
data.tar.gz: eeb6ffc30f8bfc9a9cec6ad680adcd0c7b33fed8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 316a8757c455ecd7ae0cb136a5b444e70c33fcee0263277dd287421d1360bef5e4922f31fc29877f34b92d9a2c197838f68dd45ffe6b6be3eab462ec29f360c1
|
7
|
+
data.tar.gz: 480aaa5ec37aad47601d8890be1a164e1eda342d688bb88c0a7c70d8d1d86c23a3d0272b66c1e512f6e761608dc386975e30de216ac6956511d0f970a77009f8
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# SysCmd
|
2
2
|
|
3
|
+
[](http://badge.fury.io/rb/sys_cmd)
|
4
|
+
[](https://travis-ci.org/jgoizueta/sys_cmd)
|
5
|
+
|
3
6
|
SysCmd is a DSL to define commands to be executed by the system.
|
4
7
|
|
5
8
|
The command arguments will we escaped properly for bash or
|
@@ -25,19 +28,82 @@ Or install it yourself as:
|
|
25
28
|
|
26
29
|
## Usage
|
27
30
|
|
28
|
-
|
31
|
+
A command can be defined with a simple DSL (passing a block that defines
|
32
|
+
the command arguments to the SysCmd.command method):
|
29
33
|
|
30
34
|
cmd = SysCmd.command 'ffmpeg' do
|
31
35
|
option '-i', file: 'input video file.mkv'
|
32
36
|
option '-vcodec', 'mjpeg'
|
33
37
|
file 'output.mkv'
|
34
38
|
end
|
39
|
+
|
40
|
+
The block is executed with +instance_eval+ inside the command definition
|
41
|
+
(an instance of SysCmd::Definicion), so +self+ and instance variables refer
|
42
|
+
to the definition. If this is not desirable an argument can be passed to
|
43
|
+
the block with the +Definition+ object:
|
44
|
+
|
45
|
+
cmd = SysCmd.command 'ffmpeg' do |cmd|
|
46
|
+
cmd.option '-i', file: 'input video file.mkv'
|
47
|
+
cmd.option '-vcodec', 'mjpeg'
|
48
|
+
cmd.file 'output.mkv'
|
49
|
+
end
|
50
|
+
|
51
|
+
The command can be converted to a String which represents it with
|
52
|
+
arguments quoted for the target OS/shell (here we assume a UN*X system)
|
53
|
+
|
35
54
|
puts cmd.to_s # ffmpeg -i input\ video\ file.mkv -vcodec mjpeg output.mkv
|
55
|
+
|
56
|
+
A command can be generated for a system different from the current host
|
57
|
+
by passing the +:os+ option:
|
58
|
+
|
59
|
+
wcmd = SysCmd.command 'ffmpeg', os: :windows do |cmd|
|
60
|
+
cmd.option '-i', file: 'input video file.mkv'
|
61
|
+
cmd.option '-vcodec', 'mjpeg'
|
62
|
+
cmd.file 'output.mkv'
|
63
|
+
end
|
64
|
+
puts cmd.to_s # ffmpeg -i "input video file.mkv" -vcodec mjpeg "output.mkv"
|
65
|
+
|
66
|
+
Currently only +:windows+ (for CMD.EXE syntax) and +:unix+ (for bash syntax) are
|
67
|
+
accepted for the +:os+ parameter. +:unix+ represent any UN*X-like system
|
68
|
+
(including linux, OSX, etc.)
|
69
|
+
|
70
|
+
A Command can also be executed:
|
71
|
+
|
36
72
|
cmd.run
|
37
73
|
if cmd.success?
|
38
74
|
puts cmd.output
|
39
75
|
end
|
40
76
|
|
77
|
+
By default execution is done by launching a shell to interpret the command.
|
78
|
+
Unquoted arguments will be interpreted by the shell in that case:
|
79
|
+
|
80
|
+
cmd = SysCmd.command 'echo' do
|
81
|
+
argument '$BASH'
|
82
|
+
end
|
83
|
+
cmd.run
|
84
|
+
puts cmd.output # /bin/bash
|
85
|
+
|
86
|
+
Shell execution can be avoided by passing the +:direct+ option with value
|
87
|
+
+true+ to the +run+ method. In that case the command is executed directly,
|
88
|
+
and no shell interpretation takes place, so:
|
89
|
+
|
90
|
+
cmd.run direct: true
|
91
|
+
puts cmd.output # $BASH
|
92
|
+
|
93
|
+
If the command options include
|
94
|
+
an option with the name of the command being defined it is used to
|
95
|
+
replace the command name. This can be handy to pass user configuration
|
96
|
+
to define the location/name of commands in a particular system:
|
97
|
+
|
98
|
+
options = {
|
99
|
+
curl: "/usr/local/bin/curl"
|
100
|
+
}
|
101
|
+
cmd = SysCmd.command 'curl', options do
|
102
|
+
file 'http://jsonip.com'
|
103
|
+
end
|
104
|
+
puts cmd.to_s # /usr/local/bin/curl http://jsonip.com
|
105
|
+
|
106
|
+
|
41
107
|
## Development
|
42
108
|
|
43
109
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/sys_cmd.rb
CHANGED
@@ -26,7 +26,7 @@ module SysCmd
|
|
26
26
|
@last_arg = :command
|
27
27
|
end
|
28
28
|
|
29
|
-
attr_reader :command
|
29
|
+
attr_reader :command, :shell
|
30
30
|
|
31
31
|
def to_s
|
32
32
|
command
|
@@ -124,14 +124,13 @@ module SysCmd
|
|
124
124
|
@last_arg = :file
|
125
125
|
end
|
126
126
|
|
127
|
-
# Add the value of an option
|
127
|
+
# Add the value of an option (or a quoted argument)
|
128
128
|
#
|
129
129
|
# option '-x'
|
130
130
|
# join_value 123 # -x 123
|
131
131
|
#
|
132
132
|
def value(value, options = {})
|
133
133
|
return unless @shell.applicable?(options)
|
134
|
-
raise "An option is required for value" unless @last_arg == :option
|
135
134
|
@command << ' ' << @shell.escape_filename(value.to_s)
|
136
135
|
@last_arg = :value
|
137
136
|
end
|
@@ -162,10 +161,12 @@ module SysCmd
|
|
162
161
|
@last_arg = :value
|
163
162
|
end
|
164
163
|
|
165
|
-
# Add
|
164
|
+
# Add an unquoted argument to the command.
|
165
|
+
# This is not useful for commands executed directly, since the arguments
|
166
|
+
# are note interpreted by a shell in that case.
|
166
167
|
def argument(value, options = {})
|
167
168
|
return unless @shell.applicable?(options)
|
168
|
-
@command << ' ' <<
|
169
|
+
@command << ' ' << value.to_s
|
169
170
|
@last_arg = :argument
|
170
171
|
end
|
171
172
|
|
@@ -173,8 +174,14 @@ module SysCmd
|
|
173
174
|
|
174
175
|
# An executable system command
|
175
176
|
class Command
|
176
|
-
def initialize(command)
|
177
|
-
|
177
|
+
def initialize(command, options = {})
|
178
|
+
if command.respond_to?(:shell)
|
179
|
+
@command = command.command
|
180
|
+
@shell = command.shell
|
181
|
+
else
|
182
|
+
@command = command
|
183
|
+
@shell = Shell.new(options)
|
184
|
+
end
|
178
185
|
@output = nil
|
179
186
|
@status = nil
|
180
187
|
@error_output = nil
|
@@ -185,6 +192,17 @@ module SysCmd
|
|
185
192
|
|
186
193
|
# Execute the command.
|
187
194
|
#
|
195
|
+
# By default the command is executed by a shell. In this case,
|
196
|
+
# unquoted arguments are interpreted by the shell, e.g.
|
197
|
+
#
|
198
|
+
# SysCmd.command('echo $BASH').run # /bin/bash
|
199
|
+
#
|
200
|
+
# When the +:direct+ option is set to true, no shell is used and
|
201
|
+
# the command is directly executed; in this case unquoted arguments
|
202
|
+
# are not interpreted:
|
203
|
+
#
|
204
|
+
# SysCmd.command('echo $BASH').run # $BASH
|
205
|
+
#
|
188
206
|
# The exit status of the command is retained in the +status+ attribute
|
189
207
|
# (and its numeric value in the +status_value+ attribute).
|
190
208
|
#
|
@@ -213,14 +231,19 @@ module SysCmd
|
|
213
231
|
#
|
214
232
|
def run(options = {})
|
215
233
|
@output = @status = @error_output = @error = nil
|
234
|
+
if options[:direct]
|
235
|
+
command = @shell.split(@command)
|
236
|
+
else
|
237
|
+
command = [@command]
|
238
|
+
end
|
216
239
|
begin
|
217
240
|
case options[:error_output]
|
218
241
|
when :mix # mix stderr with stdout
|
219
|
-
@output, @status = Open3.capture2e(
|
242
|
+
@output, @status = Open3.capture2e(*command)
|
220
243
|
when :separate
|
221
|
-
@output, @error_output, @status = Open3.capture3(
|
244
|
+
@output, @error_output, @status = Open3.capture3(*command)
|
222
245
|
else # :console (do not capture stderr output)
|
223
|
-
@output, @status = Open3.capture2(
|
246
|
+
@output, @status = Open3.capture2(*command)
|
224
247
|
end
|
225
248
|
rescue => error
|
226
249
|
@error = error.dup
|
@@ -265,8 +288,14 @@ module SysCmd
|
|
265
288
|
#
|
266
289
|
def self.command(command, options = {}, &block)
|
267
290
|
definition = Definition.new(command, options)
|
268
|
-
|
269
|
-
|
291
|
+
if block
|
292
|
+
if block.arity == 1
|
293
|
+
block.call definition
|
294
|
+
else
|
295
|
+
definition.instance_eval &block
|
296
|
+
end
|
297
|
+
end
|
298
|
+
Command.new definition
|
270
299
|
end
|
271
300
|
|
272
301
|
# Build and run a command
|
@@ -305,6 +334,26 @@ module SysCmd
|
|
305
334
|
end
|
306
335
|
end
|
307
336
|
|
337
|
+
def self.split(text, options = {})
|
338
|
+
case os_type(options)
|
339
|
+
when :windows
|
340
|
+
words = []
|
341
|
+
field = ''
|
342
|
+
line.scan(/\G\s*(?>([^\s\^\'\"]+)|'([^\']*)'|"((?:[^\"\^]|\\.)*)"|(\^.?)|(\S))(\s|\z)?/m) do
|
343
|
+
|word, sq, dq, esc, garbage, sep|
|
344
|
+
raise ArgumentError, "Unmatched double quote: #{line.inspect}" if garbage
|
345
|
+
field << (word || sq || (dq || esc).gsub(/\^(.)/, '\\1'))
|
346
|
+
if sep
|
347
|
+
words << field
|
348
|
+
field = ''
|
349
|
+
end
|
350
|
+
end
|
351
|
+
words
|
352
|
+
else
|
353
|
+
Shellwords.shellsplit(text)
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
308
357
|
def self.line_separator(options = {})
|
309
358
|
case os_type(options)
|
310
359
|
when :windows
|
@@ -335,6 +384,10 @@ module SysCmd
|
|
335
384
|
SysCmd.escape(text, os: @type)
|
336
385
|
end
|
337
386
|
|
387
|
+
def split(text)
|
388
|
+
SysCmd.split(text, os: @type)
|
389
|
+
end
|
390
|
+
|
338
391
|
def escape_filename(name)
|
339
392
|
escape name
|
340
393
|
end
|
data/lib/sys_cmd/version.rb
CHANGED
data/sys_cmd.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sys_cmd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Javier Goizueta
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: os
|
@@ -96,7 +96,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
96
96
|
requirements:
|
97
97
|
- - ">="
|
98
98
|
- !ruby/object:Gem::Version
|
99
|
-
version:
|
99
|
+
version: 1.9.3
|
100
100
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
101
|
requirements:
|
102
102
|
- - ">="
|
@@ -104,8 +104,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
104
104
|
version: '0'
|
105
105
|
requirements: []
|
106
106
|
rubyforge_project:
|
107
|
-
rubygems_version: 2.
|
107
|
+
rubygems_version: 2.2.2
|
108
108
|
signing_key:
|
109
109
|
specification_version: 4
|
110
110
|
summary: Execute shell commands.
|
111
111
|
test_files: []
|
112
|
+
has_rdoc:
|