hysh 0.0.1
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 +7 -0
- data/Gemfile +4 -0
- data/README.rdoc +116 -0
- data/Rakefile +17 -0
- data/lib/hysh.rb +836 -0
- data/spec/hysh_spec.rb +39 -0
- data/test/dpkg_test.rb +33 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3f13194edb86a917872e0c88c067ce33b7e18dcf
|
4
|
+
data.tar.gz: 4e743290d7746ad6fabc62deeb058f8ecfe9effd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7380c1dd990f66b059b4969f0d5a9e29165a5a207a9281c584d97497b74613f704c736a4cc1117876a1ef8ff3f6a0248500fdd46ae17d099a6d2b859852cc02c
|
7
|
+
data.tar.gz: 6186d9e71eb8148c0dc3d99755e2f89fb666818ef14c6e483cba93f73ce6f2808224ea5a3181c2769418acbcbac04459766b61b4804f825b881ce060d0d889bb
|
data/Gemfile
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
= Ruby HYSH
|
2
|
+
|
3
|
+
Ruby HYSH stands for Huang Ying's SHell in Ruby.
|
4
|
+
|
5
|
+
Bash interactive shell and scripts are very important tools to use
|
6
|
+
Linux/Unix. But I don't like the syntax of bash, would rather to do
|
7
|
+
that in Ruby. This work is based on HYSH (Huang Ying's SHell in
|
8
|
+
Common Lisp: https://github.com/hying-caritas/hysh).
|
9
|
+
|
10
|
+
== Example
|
11
|
+
|
12
|
+
def dpkg_installed(package_names = nil)
|
13
|
+
Hysh.out_lines ->{
|
14
|
+
Hysh.pipe ['dpkg', '-l'],
|
15
|
+
if package_names
|
16
|
+
['egrep', "(#{package_names.join '|'})"]
|
17
|
+
else
|
18
|
+
['cat']
|
19
|
+
end
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
or write the filter in Ruby.
|
24
|
+
|
25
|
+
def dpkg_installed(package_names = nil)
|
26
|
+
Hysh.out_lines ->{
|
27
|
+
Hysh.pipe ['dpkg', '-l'] {
|
28
|
+
proc_line = if package_names
|
29
|
+
->l{
|
30
|
+
if package_names.any? { |pkg|
|
31
|
+
l.index pkg
|
32
|
+
}
|
33
|
+
l
|
34
|
+
end
|
35
|
+
}
|
36
|
+
else
|
37
|
+
->l{ l }
|
38
|
+
end
|
39
|
+
Hysh.filter_line &proc_line
|
40
|
+
}
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
Compared with Kernel.system, HYSH provides different coding style for
|
45
|
+
IO redirection, Environment manipulating, Setting current directory,
|
46
|
+
pipe line support without shell, and writing pipeline filter in Ruby.
|
47
|
+
|
48
|
+
== Common conventions
|
49
|
+
|
50
|
+
There are mainly two categories of functions in HYSH. Some functions
|
51
|
+
compute (run a function or command), some other functions setup
|
52
|
+
environment (IO redirection, manipulating environment, changing
|
53
|
+
current directory, etc.) for computing.
|
54
|
+
|
55
|
+
Functions to setup environment for some computing have one parameter
|
56
|
+
to several parameters (sometimes via block too) to specify the one to
|
57
|
+
several computing (for example, pipe). Most computing functions
|
58
|
+
return whether computing is successful ($? holds the details status if
|
59
|
+
the computing is synchronous and run as the process). Most functions
|
60
|
+
to setup environment will return the return value of one of the given
|
61
|
+
computing. Most out_, and io_ family functions will return two values,
|
62
|
+
the first is the string returned, the second is the run value of the
|
63
|
+
computing.
|
64
|
+
|
65
|
+
== Run external program
|
66
|
+
|
67
|
+
Unlike Kernel.system, HYSH typically uses only a very basic run
|
68
|
+
function (although it is possible to specify options), because most IO
|
69
|
+
redirection and glue between programs are done in Ruby functions.
|
70
|
+
|
71
|
+
== IO redirection
|
72
|
+
|
73
|
+
IO redirection in Unix process world means replace the original
|
74
|
+
standard input, output, etc. file descriptors with file, pipe, etc.
|
75
|
+
In HYSH, IO redirection is defined for Ruby too. That means replace
|
76
|
+
the original $stdin, $stdout, and $stderr, etc. IOs with other IOs of
|
77
|
+
file, pipe, etc. So for an IO redirection, a Ruby global IO variable
|
78
|
+
name and a file descriptor number can be specified. After that is
|
79
|
+
done, all Ruby function will reference the replaced IOs for the global
|
80
|
+
IO variables, and the specified file descriptors redirection will be
|
81
|
+
setup for the external programs too.
|
82
|
+
|
83
|
+
== Process
|
84
|
+
|
85
|
+
The process in HYSH is used to represent a Ruby fork or Unix process.
|
86
|
+
The child processes will inherited all IO redirection, environment
|
87
|
+
variables, and current directory, etc. from their parent processes.
|
88
|
+
|
89
|
+
== Glue between processes
|
90
|
+
|
91
|
+
The most important glue is pipeline. I think this is the flagship of
|
92
|
+
UNIX worlds. Now we can do that in Ruby. Any processes can be
|
93
|
+
connected with pipeline, regardless Ruby fork or Unix process.
|
94
|
+
|
95
|
+
Other glue mechanisms are provided too, including and, or, and
|
96
|
+
sequence etc.
|
97
|
+
|
98
|
+
== External program error processing
|
99
|
+
|
100
|
+
External program error is defined as exiting with non-zero status,
|
101
|
+
that is, failed in UNIX sense. It can be ignored, warned or an
|
102
|
+
exception can be raised to stop running the following code.
|
103
|
+
|
104
|
+
== Arbitrary combination
|
105
|
+
|
106
|
+
The power of HYSH is that it provide a more flexible way to combine
|
107
|
+
external programs, IO redirection, glue (pipeline, etc.), etc. with
|
108
|
+
Ruby.
|
109
|
+
|
110
|
+
For example, to encapsulate some external filter program in Ruby with
|
111
|
+
string as input and output. It can be accomplished with:
|
112
|
+
|
113
|
+
(Hysh.in_s input-string (Hysh.out_s (Hysh.run filter arg1 arg2 ...)))
|
114
|
+
|
115
|
+
For given input-string and arguments, the form will return the result
|
116
|
+
string and exit success status of the filter.
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- ruby-indent-level: 2; -*-
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
require "rdoc/task"
|
4
|
+
|
5
|
+
task :default => 'spec'
|
6
|
+
|
7
|
+
desc "Run all specs"
|
8
|
+
RSpec::Core::RakeTask.new(:spec) { |t|
|
9
|
+
t.rspec_opts = "--colour"
|
10
|
+
t.pattern = "spec/*_spec.rb"
|
11
|
+
}
|
12
|
+
|
13
|
+
desc "Generate document"
|
14
|
+
RDoc::Task.new { |rdoc|
|
15
|
+
rdoc.main = "README.rdoc"
|
16
|
+
rdoc.rdoc_files.include "README.rdoc", "lib/*.rb"
|
17
|
+
}
|
data/lib/hysh.rb
ADDED
@@ -0,0 +1,836 @@
|
|
1
|
+
# -*- ruby-indent-level: 2; -*-
|
2
|
+
#
|
3
|
+
# Hysh: Huang Ying's Shell in Ruby
|
4
|
+
#
|
5
|
+
# Copyright (c) 2015 Huang Ying <huang.ying.caritas@gmail.com>
|
6
|
+
#
|
7
|
+
# LGPL v2.0 or later.
|
8
|
+
|
9
|
+
require 'tempfile'
|
10
|
+
|
11
|
+
# Hysh stands for Huang Ying's Shell in Ruby. Like other shells, it
|
12
|
+
# can redirect IO, run external programs, and glue processes (like
|
13
|
+
# pipeline), etc. One of the best stuff it can do is to write
|
14
|
+
# pipeline filter in Ruby.
|
15
|
+
#
|
16
|
+
module Hysh
|
17
|
+
# :stopdoc:
|
18
|
+
TEMP_BASE = "hysh-"
|
19
|
+
@@redirections = []
|
20
|
+
IGNORE = :ignore
|
21
|
+
WARN = :warn
|
22
|
+
RAISE = :raise
|
23
|
+
@@on_command_error = IGNORE
|
24
|
+
# :startdoc:
|
25
|
+
|
26
|
+
# :section: Common Utilities
|
27
|
+
|
28
|
+
# :call-seq:
|
29
|
+
# with_set_globals(var_name, val, ...) { ... }
|
30
|
+
#
|
31
|
+
# Set the global variable named +var_name+ (a string or symbol) to
|
32
|
+
# +val+, then run the block, return the return value of the block.
|
33
|
+
# Restore the original value of the variable upon returning.
|
34
|
+
# Multiple pairs of +var_name+ and +val+ can be specified.
|
35
|
+
def self.with_set_globals(*var_vals)
|
36
|
+
orig_var_vals = var_vals.each_slice(2).map { |var, val|
|
37
|
+
svar = var.to_s
|
38
|
+
unless svar.start_with? '$'
|
39
|
+
raise ArgumentError, "Invalid global variable name: #{svar}"
|
40
|
+
end
|
41
|
+
orig_val = eval(svar)
|
42
|
+
[svar, val, orig_val]
|
43
|
+
}
|
44
|
+
orig_var_vals.each { |var, val|
|
45
|
+
eval("#{var} = val")
|
46
|
+
}
|
47
|
+
yield
|
48
|
+
ensure
|
49
|
+
if orig_var_vals
|
50
|
+
orig_var_vals.each { |var, val, orig_val|
|
51
|
+
eval("#{var} = orig_val")
|
52
|
+
}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# :section: IO Redirection
|
57
|
+
|
58
|
+
# :call-seq:
|
59
|
+
# with_redirect_to(fd, var_name, io) { ... }
|
60
|
+
#
|
61
|
+
# Set the variable named +var_name+ (usually +$stdin+, +$stdout+,
|
62
|
+
# +$stderr, etc.) to +io+ (redirection in Ruby), and arrange to
|
63
|
+
# redirect the +fd+ (usally 0, 1, 2, etc. to +io+ for the external
|
64
|
+
# programs, then run the block, return the return value of the
|
65
|
+
# block. Restore the original value of the variable and cancel the
|
66
|
+
# arrangement to external program redirections upon returning.
|
67
|
+
def self.with_redirect_to(fd, var, io, &b)
|
68
|
+
@@redirections.push([fd, io]) if fd
|
69
|
+
if var
|
70
|
+
with_set_globals(var, io, &b)
|
71
|
+
else
|
72
|
+
yield
|
73
|
+
end
|
74
|
+
ensure
|
75
|
+
@@redirections.pop if fd
|
76
|
+
end
|
77
|
+
|
78
|
+
# :call-seq:
|
79
|
+
# with_redirect_stdin_to(io) { ... }
|
80
|
+
#
|
81
|
+
# Set the +$stdin+ to +io+ (redirection in Ruby), and arrange to
|
82
|
+
# redirect the file descriptor 0 to +io+ for the external programs,
|
83
|
+
# then run the block, return the return value of the block. Restore
|
84
|
+
# the original value of the $stdin and cancel the arrangement to
|
85
|
+
# external program redirections upon returning.
|
86
|
+
def self.with_redirect_stdin_to(io, &b)
|
87
|
+
with_redirect_to(0, :$stdin, io, &b)
|
88
|
+
end
|
89
|
+
|
90
|
+
# :call-seq:
|
91
|
+
# with_redirect_stdout_to(io) { ... }
|
92
|
+
#
|
93
|
+
# Set the +$stdout+ to +io+ (redirection in Ruby), and arrange to
|
94
|
+
# redirect the file descriptor 1 to +io+ for the external programs,
|
95
|
+
# then run the block, return the return value of the block. Restore
|
96
|
+
# the original value of the $stdout and cancel the arrangement to
|
97
|
+
# external program redirections upon returning.
|
98
|
+
def self.with_redirect_stdout_to(io, &b)
|
99
|
+
with_redirect_to(1, :$stdout, io, &b)
|
100
|
+
end
|
101
|
+
|
102
|
+
# :call-seq:
|
103
|
+
# with_redirect_stderr_to(io) { ... }
|
104
|
+
#
|
105
|
+
# Set the +$stderr+ to +io+ (redirection in Ruby), and arrange to
|
106
|
+
# redirect the file descriptor 2 to +io+ for the external programs,
|
107
|
+
# then run the block, return the return value of the block. Restore
|
108
|
+
# the original value of the $stderr and cancel the arrangement to
|
109
|
+
# external program redirections upon returning.
|
110
|
+
def self.with_redirect_stderr_to(io, &b)
|
111
|
+
with_redirect_to(2, :$stderr, io, &b)
|
112
|
+
end
|
113
|
+
|
114
|
+
# :call-seq:
|
115
|
+
# with_redirect_stdin_file(args...) { ... }
|
116
|
+
#
|
117
|
+
# Open the file with parameters: +args+, which are same as the
|
118
|
+
# parameters of +File.open+. Set the +$stdin+ to the return +io+
|
119
|
+
# (redirection in Ruby), and arrange to redirect the file descriptor
|
120
|
+
# 0 to the returned +io+ for the external programs, then run the
|
121
|
+
# block, return the return value of the block. Restore the original
|
122
|
+
# value of the $stdin and cancel the arrangement to external program
|
123
|
+
# redirections upon returning.
|
124
|
+
def self.with_redirect_stdin_to_file(*args, &b)
|
125
|
+
File.open(*args) { |f|
|
126
|
+
with_redirect_stdin_to f, &b
|
127
|
+
}
|
128
|
+
end
|
129
|
+
|
130
|
+
# :call-seq:
|
131
|
+
# with_redirect_stdout_file(args...) { ... }
|
132
|
+
#
|
133
|
+
# Open the file with parameters: +args+, which are same as the
|
134
|
+
# parameters of +File.open+. Set the +$stdout+ to the return +io+
|
135
|
+
# (redirection in Ruby), and arrange to redirect the file descriptor
|
136
|
+
# 1 to the returned +io+ for the external programs, then run the
|
137
|
+
# block, return the return value of the block. Restore the original
|
138
|
+
# value of the $stdout and cancel the arrangement to external
|
139
|
+
# program redirections upon returning.
|
140
|
+
def self.with_redirect_stdout_to_file(*args, &b)
|
141
|
+
if args.size == 1
|
142
|
+
args.push "w"
|
143
|
+
end
|
144
|
+
File.open(*args) { |f|
|
145
|
+
with_redirect_stdout_to f, &b
|
146
|
+
}
|
147
|
+
end
|
148
|
+
|
149
|
+
# :call-seq:
|
150
|
+
# with_redirect_stderr_file(args...) { ... }
|
151
|
+
#
|
152
|
+
# Open the file with parameters: +args+, which are same as the
|
153
|
+
# parameters of +File.open+. Set the +$stderr+ to the return +io+
|
154
|
+
# (redirection in Ruby), and arrange to redirect the file descriptor
|
155
|
+
# 2 to the returned +io+ for the external programs, then run the
|
156
|
+
# block, return the return value of the block. Restore the original
|
157
|
+
# value of the $stderr and cancel the arrangement to external
|
158
|
+
# program redirections upon returning.
|
159
|
+
def self.with_redirect_stderr_to_file(*args, &b)
|
160
|
+
if args.size == 1
|
161
|
+
args.push "w"
|
162
|
+
end
|
163
|
+
File.open(*args) { |f|
|
164
|
+
with_redirect_stderr_to f, &b
|
165
|
+
}
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.__out_io(args, options, proc_arg) # :nodoc:
|
169
|
+
Tempfile.open(TEMP_BASE) { |tempf|
|
170
|
+
tempf.unlink
|
171
|
+
ret = nil
|
172
|
+
with_redirect_stdout_to(tempf) {
|
173
|
+
ret = __run args, options, proc_arg
|
174
|
+
}
|
175
|
+
tempf.rewind
|
176
|
+
stuff = yield tempf
|
177
|
+
[stuff, ret]
|
178
|
+
}
|
179
|
+
end
|
180
|
+
|
181
|
+
# :call-seq:
|
182
|
+
# out_s() { ... } -> [string, any]
|
183
|
+
# out_s(function) -> [string, any]
|
184
|
+
# out_s(command...[, options]) -> [string, true or false]
|
185
|
+
#
|
186
|
+
# Collect the output of running the block, or the function specified
|
187
|
+
# via +function+ or the external program specified via +command+ and
|
188
|
+
# +options+ via stdout redirection. +command+ and +options+
|
189
|
+
# parameters are same as that of +Process.spawn+. Return the
|
190
|
+
# collected output string and the return value of the block or the
|
191
|
+
# function or exit success status of the external program as a two
|
192
|
+
# element array. Restore stdout redirection upon returning.
|
193
|
+
def self.out_s(*args, &blk)
|
194
|
+
__out_io(*__parse_args(args, blk)) { |tempf|
|
195
|
+
tempf.read
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
199
|
+
# :call-seq:
|
200
|
+
# out_ss() { ... } -> [string, any]
|
201
|
+
# out_ss(function) -> [string, any]
|
202
|
+
# out_ss(command...[, options]) -> [string, true or false]
|
203
|
+
#
|
204
|
+
# Same as out_s, except the collected output string are right
|
205
|
+
# stripped before return.
|
206
|
+
def self.out_ss(*args_in, &blk)
|
207
|
+
s, ret = out_s(*args_in, &blk)
|
208
|
+
[s.rstrip, ret]
|
209
|
+
end
|
210
|
+
|
211
|
+
# :call-seq:
|
212
|
+
# out_lines(funciton) -> [array of string, any]
|
213
|
+
# out_lines(command...[, options]) -> [array of string, any]
|
214
|
+
# out_lines(function) { |line| ... } -> true or false
|
215
|
+
# out_lines(command...[, options]) { |line| ... } -> true or false
|
216
|
+
#
|
217
|
+
# If no block is supplied, collect the output of running the
|
218
|
+
# function specified via +function+ or the external program specified
|
219
|
+
# via +command+ and +options+ via stdout redirection. +command+ and
|
220
|
+
# +options+ are same as that of +Process.spawn+. Return the
|
221
|
+
# collected string as lines and the return value of the block or the
|
222
|
+
# function or exit success status of the external program. Restore
|
223
|
+
# stdout redirection upon returning.
|
224
|
+
#
|
225
|
+
# If block is supplied, collect the output of running the function
|
226
|
+
# specified via +function+ (in a forked sub-process) or the external
|
227
|
+
# program specified via +command+ and +options+ via stdout
|
228
|
+
# redirection. +command+ and +options+ are same as that of
|
229
|
+
# +Process.spawn+. Feed each line of output to the block as +line+.
|
230
|
+
# Return the exit success status of the forked sub-process or the
|
231
|
+
# external program. Restore stdout redirection upon returning.
|
232
|
+
def self.out_lines(*args_in, &blk)
|
233
|
+
args, options, proc_arg = __parse_args args_in
|
234
|
+
if block_given?
|
235
|
+
__popen(nil, true, nil, args, options, proc_arg) { |pid, stdin, stdout, stderr|
|
236
|
+
stdout.each_line(&blk)
|
237
|
+
Process.waitpid pid
|
238
|
+
__check_command_status args_in
|
239
|
+
}
|
240
|
+
else
|
241
|
+
__out_io(args, options, proc_arg) { |tempf|
|
242
|
+
tempf.readlines
|
243
|
+
}
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# :call-seq:
|
248
|
+
# out_err_s() { ... } -> [string, any]
|
249
|
+
# out_err_s(function) -> [string, any]
|
250
|
+
# out_err_s(command...[, options]) -> [string, true or false]
|
251
|
+
#
|
252
|
+
# Same as out_s, except collect output of stderr too.
|
253
|
+
def self.out_err_s(*args_in, &blk)
|
254
|
+
args, options, proc_arg = __parse_args args_in, blk
|
255
|
+
Tempfile.open(TEMP_BASE) { |tempf|
|
256
|
+
tempf.unlink
|
257
|
+
ret = nil
|
258
|
+
with_redirect_stdout_to(tempf) {
|
259
|
+
with_redirect_stderr_to(tempf) {
|
260
|
+
ret = __run args, options, proc_arg
|
261
|
+
}
|
262
|
+
}
|
263
|
+
tempf.rewind
|
264
|
+
s = tempf.read
|
265
|
+
[s, ret]
|
266
|
+
}
|
267
|
+
end
|
268
|
+
|
269
|
+
# :call-seq:
|
270
|
+
# out_err_ss() { ... } -> [string, any]
|
271
|
+
# out_err_ss(function) -> [string, any]
|
272
|
+
# out_err_ss(command...[, options]) -> [string, true or false]
|
273
|
+
#
|
274
|
+
# Same as out_err_s, except the collected output string are right
|
275
|
+
# stripped before return.
|
276
|
+
def self.out_err_ss(*args_in, &blk)
|
277
|
+
s, ret = out_err_s(*args_in, &blk)
|
278
|
+
[s.rstrip. ret]
|
279
|
+
end
|
280
|
+
|
281
|
+
def self.__in_io(args, options, proc_arg) # :nodoc:
|
282
|
+
Tempfile.open(TEMP_BASE) { |tempf|
|
283
|
+
tempf.unlink
|
284
|
+
yield tempf
|
285
|
+
tempf.rewind
|
286
|
+
with_redirect_stdin_to(tempf) {
|
287
|
+
__run args, options, proc_arg
|
288
|
+
}
|
289
|
+
}
|
290
|
+
end
|
291
|
+
|
292
|
+
# :call-seq:
|
293
|
+
# in_s(string) { ... } -> any
|
294
|
+
# in_s(string, function) -> any
|
295
|
+
# in_s(string, command...[, options]) -> true or false
|
296
|
+
#
|
297
|
+
# Feed the string specified via +string+ to the running of the
|
298
|
+
# block, or the function specified via +function+ or the external
|
299
|
+
# program specified via +command+ and +options+ via stdin
|
300
|
+
# redirection. +command+ and +options+ are same as that of
|
301
|
+
# +Process.spawn+. Return the return value of the block or the
|
302
|
+
# function or the exit success status of the external program.
|
303
|
+
# Restore stdin redirection upon returning.
|
304
|
+
def self.in_s(s, *args_in, &blk)
|
305
|
+
args, options, proc_arg = __parse_args args_in, blk
|
306
|
+
__in_io(args, options, proc_arg) { |tempf|
|
307
|
+
tempf.write s
|
308
|
+
}
|
309
|
+
end
|
310
|
+
|
311
|
+
# :call-seq:
|
312
|
+
# in_lines(lines) { ... } -> any
|
313
|
+
# in_lines(lines, function) -> any
|
314
|
+
# in_lines(lines, command...[, options]) -> true or false
|
315
|
+
#
|
316
|
+
# Same as +in_s+, except input string are specified via +lines+
|
317
|
+
# (Array of String).
|
318
|
+
def self.in_lines(lines, *args_in, &blk)
|
319
|
+
args, options, proc_arg = __parse_args args_in, blk
|
320
|
+
__in_io(args, options, proc_arg) { |tempf|
|
321
|
+
lines.each { |line| tempf.write line }
|
322
|
+
}
|
323
|
+
end
|
324
|
+
|
325
|
+
# :call-seq:
|
326
|
+
# io_s(string) { ... } -> [string, any]
|
327
|
+
# io_s(string, function) -> [string, any]
|
328
|
+
# io_s(stirng, command...[, options]) -> [string, true or false]
|
329
|
+
#
|
330
|
+
# Redirect the stdin and stdout like that of +in_s+ and +out_s+,
|
331
|
+
# return value is same of +out_s+.
|
332
|
+
def self.io_s(s, *args_in, &blk)
|
333
|
+
in_s(s) {
|
334
|
+
out_s {
|
335
|
+
run *args_in, &blk
|
336
|
+
}
|
337
|
+
}
|
338
|
+
end
|
339
|
+
|
340
|
+
# :call-seq:
|
341
|
+
# io_ss(string) { ... } -> [string, any]
|
342
|
+
# io_ss(string, function) -> [string, any]
|
343
|
+
# io_ss(stirng, command...[, options]) -> [string, true or false]
|
344
|
+
#
|
345
|
+
# Same as +io_s+, except the output string is right stripped before
|
346
|
+
# returning.
|
347
|
+
def self.io_ss(s, *args_in, &blk)
|
348
|
+
s = io_s(s, *args_in, &blk)
|
349
|
+
s.rstrip
|
350
|
+
end
|
351
|
+
|
352
|
+
# :section: Run Process
|
353
|
+
|
354
|
+
# :call-seq:
|
355
|
+
# ignore_on_command_error() { ... }
|
356
|
+
#
|
357
|
+
# When running the block, the non-zero exit status of running
|
358
|
+
# external program are ignored. The original behavior is restored
|
359
|
+
# upon returning.
|
360
|
+
def self.ignore_on_command_error(&b)
|
361
|
+
with_set_globals(:@@on_command_error, IGNORE, &b)
|
362
|
+
end
|
363
|
+
|
364
|
+
# :call-seq:
|
365
|
+
# warn_on_command_error() { ... }
|
366
|
+
#
|
367
|
+
# When running the block, the warning message will be print to
|
368
|
+
# $stderr when the external program exited with non-zero status.
|
369
|
+
# The original behavior is restored upon returning.
|
370
|
+
def self.warn_on_command_error(&b)
|
371
|
+
with_set_globals(:@@on_command_error, WARN, &b)
|
372
|
+
end
|
373
|
+
|
374
|
+
# :call-seq:
|
375
|
+
# raise_on_command_error() { ... }
|
376
|
+
#
|
377
|
+
# When running the block, an +Hysh::CommandError+ exception will be
|
378
|
+
# raised when the external program exited with non-zero status. The
|
379
|
+
# original behavior is restored upon returning.
|
380
|
+
def self.raise_on_command_error(&b)
|
381
|
+
with_set_globals(:@@on_command_error, RAISE, &b)
|
382
|
+
end
|
383
|
+
|
384
|
+
def self.__parse_args(args, blk = nil) # :nodoc:
|
385
|
+
args = [args] unless args.is_a? Array
|
386
|
+
if args.last.is_a?(Hash)
|
387
|
+
options = args.pop
|
388
|
+
else
|
389
|
+
options = {}
|
390
|
+
end
|
391
|
+
if args.empty?
|
392
|
+
if blk.equal? nil
|
393
|
+
raise ArgumentError.new('No argument or block!')
|
394
|
+
else
|
395
|
+
args = [blk]
|
396
|
+
proc_arg = true
|
397
|
+
end
|
398
|
+
else
|
399
|
+
proc_arg = args.size == 1 && args.first.is_a?(Proc)
|
400
|
+
end
|
401
|
+
[args, options, proc_arg]
|
402
|
+
end
|
403
|
+
|
404
|
+
# :call-seq:
|
405
|
+
# with_change_env(env_var, val, ...) { ... }
|
406
|
+
#
|
407
|
+
# When running the block, the environment will be changed as
|
408
|
+
# specified via parameters. The +env_var+ specifies the environment
|
409
|
+
# variable name, and the +val+ specifies the value, when +val+ is
|
410
|
+
# nil, the envioronment variable will be removed. Multiple pairs of
|
411
|
+
# the environment variable names and values can be specified. The
|
412
|
+
# changes to the environment are restored upon returning.
|
413
|
+
def self.with_change_env(*var_vals)
|
414
|
+
orig_var_vals = var_vals.each_slice(2).map { |var, val|
|
415
|
+
orig_val = ENV[var]
|
416
|
+
[var, orig_val]
|
417
|
+
}
|
418
|
+
var_vals.each_slice(2) { |var, val|
|
419
|
+
ENV[var] = val
|
420
|
+
}
|
421
|
+
yield
|
422
|
+
ensure
|
423
|
+
if orig_var_vals
|
424
|
+
orig_var_vals.each { |var, orig_val|
|
425
|
+
ENV[var] = orig_val
|
426
|
+
}
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
# :call-seq:
|
431
|
+
# chdir(dir) { ... }
|
432
|
+
#
|
433
|
+
# Same as +Dir.chdir+.
|
434
|
+
def self.chdir(dir, &b)
|
435
|
+
Dir.chdir(dir, &b)
|
436
|
+
end
|
437
|
+
|
438
|
+
def self.__spawn(args, options_in, proc_arg) # :nodoc:
|
439
|
+
if proc_arg
|
440
|
+
Process.fork {
|
441
|
+
fclose = options_in[:close] || []
|
442
|
+
fclose.each { |f| f.close }
|
443
|
+
fin = options_in[0]
|
444
|
+
fout = options_in[1]
|
445
|
+
fd_in, var_in = fin ? [0, :$stdin] : [nil, nil]
|
446
|
+
fd_out, var_out = fout ? [1, :$stdout] : [nil, nil]
|
447
|
+
with_redirect_to(fd_in, var_in, fin) {
|
448
|
+
with_redirect_to(fd_out, var_out, fout) {
|
449
|
+
begin
|
450
|
+
exit 1 unless args.first.()
|
451
|
+
rescue => e
|
452
|
+
$stderr.puts e
|
453
|
+
$stderr.puts e.backtrace
|
454
|
+
exit 1
|
455
|
+
end
|
456
|
+
}
|
457
|
+
}
|
458
|
+
}
|
459
|
+
else
|
460
|
+
options = Hash[@@redirections]
|
461
|
+
options[:close_others] = true
|
462
|
+
options.merge! options_in
|
463
|
+
Process.spawn(*args, options)
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
# :call-seq:
|
468
|
+
# spawn() { ... } -> pid
|
469
|
+
# spawn(function) -> pid
|
470
|
+
# spawn(command...[, options]) -> pid
|
471
|
+
#
|
472
|
+
# Run the block or the function specified via +function+ in a forked
|
473
|
+
# sub-process, or run external program specified via +command+ and
|
474
|
+
# +options+, +command+ and +options+ are same as that of
|
475
|
+
# Process.spawn. Return the +pid+.
|
476
|
+
def self.spawn(*args_in, &blk)
|
477
|
+
__spawn *__parse_args(args_in, blk)
|
478
|
+
end
|
479
|
+
|
480
|
+
# Exception class raised when an external program exits with
|
481
|
+
# non-zero status and raise_on_command_error take effect.
|
482
|
+
class CommandError < StandardError
|
483
|
+
# :call-seq:
|
484
|
+
# CommandError.new(cmdline, status)
|
485
|
+
#
|
486
|
+
# Create an instance of CommandError class, for the external
|
487
|
+
# program command line specified via +cmdline+ as an array of
|
488
|
+
# string and failed exit status specified via +status+ as
|
489
|
+
# Process::Status.
|
490
|
+
def initialize(cmdline, status)
|
491
|
+
@cmdline = cmdline
|
492
|
+
@status = status
|
493
|
+
reason = if status.exited?
|
494
|
+
"exited with #{status.exitstatus}"
|
495
|
+
else
|
496
|
+
"kill by #{status.termsig}"
|
497
|
+
end
|
498
|
+
super "#{cmdline}: #{reason}"
|
499
|
+
end
|
500
|
+
|
501
|
+
# External program command line as an array of string.
|
502
|
+
attr_reader :cmdline
|
503
|
+
# External program exit status, as Process:Status
|
504
|
+
attr_reader :status
|
505
|
+
end
|
506
|
+
|
507
|
+
def self.__check_command_status(cmd) # :nodoc:
|
508
|
+
unless $?.success?
|
509
|
+
if @@on_command_error != IGNORE
|
510
|
+
err = CommandError.new(cmd, $?)
|
511
|
+
case @@on_command_error
|
512
|
+
when WARN
|
513
|
+
$stderr.puts "Hysh: Command Error: #{err.to_s}"
|
514
|
+
when RAISE
|
515
|
+
raise err
|
516
|
+
end
|
517
|
+
end
|
518
|
+
false
|
519
|
+
else
|
520
|
+
true
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
def self.__run(args, options, proc_arg) #:nodoc:
|
525
|
+
if proc_arg
|
526
|
+
args.first.()
|
527
|
+
else
|
528
|
+
pid = __spawn args, options, proc_arg
|
529
|
+
Process.waitpid pid
|
530
|
+
__check_command_status(args)
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
# :call-seq:
|
535
|
+
# run() { ... } -> any
|
536
|
+
# run(function) -> any
|
537
|
+
# run(command...[, options]) -> true or false
|
538
|
+
#
|
539
|
+
# Run the block or the function specified via +function+ and return
|
540
|
+
# their return value. Or run external program specified via
|
541
|
+
# +command+ and +options+, +command+ and +options+ are same as that
|
542
|
+
# of Process.spawn and return whether external program the exit with
|
543
|
+
# 0. All IO redirections, environment change, current directory
|
544
|
+
# change, etc. will take effect when running the block, the function
|
545
|
+
# and the external program.
|
546
|
+
def self.run(*args_in, &blk)
|
547
|
+
__run *__parse_args(args_in, blk)
|
548
|
+
end
|
549
|
+
|
550
|
+
def self.__check_close(*ios) # :nodoc:
|
551
|
+
ios.each { |io|
|
552
|
+
if io && !io.closed?
|
553
|
+
io.close
|
554
|
+
end
|
555
|
+
}
|
556
|
+
end
|
557
|
+
|
558
|
+
def self.__popen(stdin, stdout, stderr, args, options, proc_arg) # :nodoc:
|
559
|
+
options[:close] = [] if proc_arg
|
560
|
+
|
561
|
+
stdin_in = stdin_out = nil
|
562
|
+
stdout_in = stdout_out = nil
|
563
|
+
stderr_in = stderr_out = nil
|
564
|
+
begin
|
565
|
+
if stdin
|
566
|
+
stdin_in, stdin_out = IO.pipe
|
567
|
+
options[0] = stdin_in
|
568
|
+
options[:close].push stdin_out if proc_arg
|
569
|
+
end
|
570
|
+
if stdout
|
571
|
+
stdout_in, stdout_out = IO.pipe
|
572
|
+
options[1] = stdout_out
|
573
|
+
options[:close].push stdout_in if proc_arg
|
574
|
+
end
|
575
|
+
if stderr == :stdout
|
576
|
+
raise ArgumentError.new unless stdout
|
577
|
+
options[2] = stdout_out
|
578
|
+
elsif stderr
|
579
|
+
stderr_in, stderr_out = IO.pipe
|
580
|
+
options[2] = stderr_out
|
581
|
+
end
|
582
|
+
pid = __spawn args, options, proc_arg
|
583
|
+
rescue
|
584
|
+
__check_close stdin_out, stdout_in, stderr_in
|
585
|
+
raise
|
586
|
+
ensure
|
587
|
+
__check_close stdin_in, stdout_out, stderr_out
|
588
|
+
end
|
589
|
+
values = [pid, stdin_out, stdout_in, stderr_in]
|
590
|
+
if block_given?
|
591
|
+
begin
|
592
|
+
yield *values
|
593
|
+
ensure
|
594
|
+
unless $?.pid == pid
|
595
|
+
Process.detach(pid) rescue nil
|
596
|
+
end
|
597
|
+
__check_close stdin_out, stdout_in, stderr_in
|
598
|
+
end
|
599
|
+
else
|
600
|
+
values
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
# :call-seq:
|
605
|
+
# popen(stdin, stdout, stderr, function) { |pid, stdin_pipe, stdout_pipe, stderr_pipe| ... }
|
606
|
+
# popen(stdin, stdout, stderr, function) -> [pid, stdin_pipe, stdout_pipe, stderr_pipe]
|
607
|
+
# popen(stdin, stdout, stderr, command...[,options]) { |pid, stdin_pipe, stdout_pipe, stderr_pipe| ... }
|
608
|
+
# popen(stdin, stdout, stderr, command...[,options]) -> [pid, stdin_pipe, stdout_pipe, stderr_pipe]
|
609
|
+
#
|
610
|
+
# Run the function specified via +function+ in a forked sub-process,
|
611
|
+
# or run external program specified via +command+ and +options+,
|
612
|
+
# +command+ and +options+ are same as that of Process.spawn.
|
613
|
+
# Redirect IO as specified via +stdin+, +stdout+, and +stderr+, any
|
614
|
+
# non-nil/false value will cause corresponding standard IO to be
|
615
|
+
# redirected to a pipe, the other end of pipe will be the block
|
616
|
+
# parameters or returned. If the value of +stderr+ argument is
|
617
|
+
# :stdout, the standard error will be redirected to standard output.
|
618
|
+
#
|
619
|
+
# If block is given, the pid and stdin, stdout and stderr pipe will
|
620
|
+
# be the parameters for the block. Popen will return the return
|
621
|
+
# value of the block. The stdin, stdout and sterr pipe will be
|
622
|
+
# closed and the process will be detached if necessary upon
|
623
|
+
# returning.
|
624
|
+
#
|
625
|
+
# If no block is given, the pid and stdin, stdout and stderr pipe
|
626
|
+
# will be returned.
|
627
|
+
def self.popen(stdin, stdout, stderr, *args_in, &blk)
|
628
|
+
args, options, proc_arg = __parse_args args_in
|
629
|
+
options[:close] = [] if proc_arg
|
630
|
+
|
631
|
+
__popen(stdin, stdout, stderr, args, options, proc_arg, &blk)
|
632
|
+
end
|
633
|
+
|
634
|
+
# :section: Glue Processes
|
635
|
+
|
636
|
+
# :call-seq:
|
637
|
+
# pipe(command_line, ...) { ... } -> any
|
638
|
+
# pipe(command_line, ...) -> any or true or false
|
639
|
+
#
|
640
|
+
# Run multiple functions or external commands specified via
|
641
|
+
# +command_line+ and the block, all functions will be run in forked
|
642
|
+
# process except the it is specified via the last argument without
|
643
|
+
# block or it is specified via the block. The stdout of the
|
644
|
+
# previous command will be connected with the stdin of the next
|
645
|
+
# command (the current process if the last argument is function
|
646
|
+
# without block or the block), that is, a pipeline is constructed to
|
647
|
+
# run the commands. If the last argument specifies a function
|
648
|
+
# without block or there is a block, return the return value of the
|
649
|
+
# function or the block. Otherwise, return the exit success status
|
650
|
+
# of the last external program.
|
651
|
+
#
|
652
|
+
# +command_line+ could be
|
653
|
+
# [command, ..., options] # command with argument and options in an array
|
654
|
+
# [command, ...] # command with/without arguments in an array
|
655
|
+
# command # command without argument
|
656
|
+
# [function] # function in an array
|
657
|
+
# function # function
|
658
|
+
def self.pipe(*cmds, &blk)
|
659
|
+
if block_given?
|
660
|
+
cmds.push [blk]
|
661
|
+
end
|
662
|
+
if cmds.empty?
|
663
|
+
raise ArgumentError.new('No argument or block!')
|
664
|
+
elsif cmds.size == 1
|
665
|
+
__run *__parse_args(cmds.first)
|
666
|
+
else
|
667
|
+
begin
|
668
|
+
pin = pout = prev_pout = last_pin = nil
|
669
|
+
|
670
|
+
last_args, last_options, last_proc_arg = __parse_args cmds.last
|
671
|
+
pin, pout = IO.pipe
|
672
|
+
if last_proc_arg
|
673
|
+
closefs = [pin]
|
674
|
+
last_pin = pin
|
675
|
+
else
|
676
|
+
last_options[0] = pin
|
677
|
+
last_pid = __spawn last_args, last_options, false
|
678
|
+
closefs = []
|
679
|
+
pin.close
|
680
|
+
end
|
681
|
+
pin = nil
|
682
|
+
|
683
|
+
cmds[1..cmds.size-2].reverse.each { |cmd|
|
684
|
+
args, options, proc_arg = __parse_args cmd
|
685
|
+
prev_pout = pout
|
686
|
+
pout = nil
|
687
|
+
pin, pout = IO.pipe
|
688
|
+
options[0] = pin
|
689
|
+
options[1] = prev_pout
|
690
|
+
if proc_arg
|
691
|
+
options[:close] = closefs + [pout]
|
692
|
+
end
|
693
|
+
pid = __spawn args, options, proc_arg
|
694
|
+
Process.detach pid
|
695
|
+
pin.close
|
696
|
+
pin = nil
|
697
|
+
prev_pout.close
|
698
|
+
prev_pout = nil
|
699
|
+
}
|
700
|
+
|
701
|
+
args, options, proc_arg = __parse_args cmds.first
|
702
|
+
options[1] = pout
|
703
|
+
if proc_arg
|
704
|
+
options[:close] = closefs
|
705
|
+
end
|
706
|
+
pid = __spawn args, options, proc_arg
|
707
|
+
pout.close
|
708
|
+
pout = nil
|
709
|
+
Process.detach pid
|
710
|
+
|
711
|
+
if last_proc_arg
|
712
|
+
ret = nil
|
713
|
+
with_redirect_stdin_to(last_pin) {
|
714
|
+
ret = __run last_args, last_options, true
|
715
|
+
}
|
716
|
+
last_pin.close
|
717
|
+
last_pin = nil
|
718
|
+
ret
|
719
|
+
else
|
720
|
+
Process.waitpid last_pid
|
721
|
+
__check_command_status cmds
|
722
|
+
end
|
723
|
+
ensure
|
724
|
+
__check_close pin, pout, prev_pout, last_pin
|
725
|
+
end
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
729
|
+
# :call-seq:
|
730
|
+
# run_seq(command_line, ...) { ... } -> any
|
731
|
+
# run_seq(command_line, ...) -> any or true or false
|
732
|
+
#
|
733
|
+
# Run the functions and the external programs specified via
|
734
|
+
# +command_line+, and the block if given, from left to right.
|
735
|
+
# +command_line+ is same as that of +pipe+. Return the return value
|
736
|
+
# or exit success status of the last function or external command.
|
737
|
+
def self.run_seq(*cmds, &blk)
|
738
|
+
if block_given?
|
739
|
+
cmds.push blk
|
740
|
+
end
|
741
|
+
ret = true
|
742
|
+
cmds.each { |cmd|
|
743
|
+
ret = run(cmd)
|
744
|
+
}
|
745
|
+
ret
|
746
|
+
end
|
747
|
+
|
748
|
+
# :call-seq:
|
749
|
+
# run_or(command_line, ...) { ... } -> any
|
750
|
+
# run_or(command_line, ...) -> any or true or false
|
751
|
+
#
|
752
|
+
# Run the functions and the external programs specified via
|
753
|
+
# +command_line+, and the block if given, from left to right.
|
754
|
+
# +command_line+ is same as that of +pipe+. If any function or
|
755
|
+
# block returns non-nil/false, or any external program exits
|
756
|
+
# successfully, stop running the remaining function, or external
|
757
|
+
# program and return the value. If all failed, false or nil will be
|
758
|
+
# returned. If no function, external program, or block is given,
|
759
|
+
# return false.
|
760
|
+
def self.run_or(*cmds, &blk)
|
761
|
+
if block_given?
|
762
|
+
cmds.push blk
|
763
|
+
end
|
764
|
+
return false if cmds.empty?
|
765
|
+
|
766
|
+
*head_cmds, last_cmd = cmds
|
767
|
+
ignore_on_command_error {
|
768
|
+
head_cmds.each { |cmd|
|
769
|
+
if ret = run(cmd)
|
770
|
+
return ret
|
771
|
+
end
|
772
|
+
}
|
773
|
+
}
|
774
|
+
run(last_cmd)
|
775
|
+
end
|
776
|
+
|
777
|
+
# :call-seq:
|
778
|
+
# run_and(command_line, ...) { ... } -> any
|
779
|
+
# run_and(command_line, ...) -> any or true or false
|
780
|
+
#
|
781
|
+
# Run the functions and the external programs specified via
|
782
|
+
# +command_line+, and the block if given, from left to right.
|
783
|
+
# +command_line+ is same as that of +pipe+. If any function or
|
784
|
+
# block returns nil or false, or any external program exits failed,
|
785
|
+
# stop running the remaining function, or external program and
|
786
|
+
# return the value. If all succeed, return the return value of the
|
787
|
+
# last function, the block or the exit success status of the
|
788
|
+
# external program. If no function, external program, or block is
|
789
|
+
# provided, return true.
|
790
|
+
def self.run_and(*cmds, &blk)
|
791
|
+
if block_given?
|
792
|
+
cmds.push blk
|
793
|
+
end
|
794
|
+
return true if cmds.empty?
|
795
|
+
|
796
|
+
*head_cmds, last_cmd = cmds
|
797
|
+
ignore_on_command_error {
|
798
|
+
head_cmds.each { |cmd|
|
799
|
+
unless ret = run(cmd)
|
800
|
+
return ret
|
801
|
+
end
|
802
|
+
}
|
803
|
+
}
|
804
|
+
run(last_cmd)
|
805
|
+
end
|
806
|
+
|
807
|
+
# :section: Filter Helpers
|
808
|
+
|
809
|
+
# :call-seq:
|
810
|
+
# filter_line() { |line| ... } -> true
|
811
|
+
#
|
812
|
+
# Feed each line from $stdin to the block, if non-nil/false
|
813
|
+
# returned, write the return value to the $stdout.
|
814
|
+
def self.filter_line
|
815
|
+
$stdin.each_line { |line|
|
816
|
+
if ret_line = yield(line)
|
817
|
+
$stdout.write ret_line
|
818
|
+
end
|
819
|
+
}
|
820
|
+
true
|
821
|
+
end
|
822
|
+
|
823
|
+
# :call-seq:
|
824
|
+
# filter_char() { |char| ... } -> true
|
825
|
+
#
|
826
|
+
# Feed each character from $stdin to the block, if non-nil/false
|
827
|
+
# returned, write the return value to the $stdout.
|
828
|
+
def self.filter_char
|
829
|
+
$stdin.each_char { |ch|
|
830
|
+
if ret_ch = yield(ch)
|
831
|
+
$stdout.write ret_ch
|
832
|
+
end
|
833
|
+
}
|
834
|
+
true
|
835
|
+
end
|
836
|
+
end
|
data/spec/hysh_spec.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- ruby-indent-level: 2; -*-
|
2
|
+
|
3
|
+
require_relative "../lib/hysh.rb"
|
4
|
+
|
5
|
+
describe Hysh do
|
6
|
+
describe ".run" do
|
7
|
+
it "run command and return whether exit with 0" do
|
8
|
+
expect(Hysh.run('true')).to eql(true)
|
9
|
+
expect(Hysh.run('false')).to eql(false)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "call ruby function" do
|
13
|
+
expect(Hysh.run { true }).to eql(true)
|
14
|
+
expect(Hysh.run { false }).to eql(false)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe ".out_s" do
|
19
|
+
it "run command and return its output" do
|
20
|
+
expect(Hysh.out_s("echo", "-n", "abc")).to eql(["abc", true])
|
21
|
+
end
|
22
|
+
|
23
|
+
it "run ruby function in process and return its output" do
|
24
|
+
expect(Hysh.out_s ->{ $stdout.write "abc" }).to eql(["abc", 3])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe ".io_s" do
|
29
|
+
it "run command, given input and return its output" do
|
30
|
+
expect(Hysh.io_s("abc", "tr", "ab", "AB")).to eql(["ABc", true])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe ".pipe" do
|
35
|
+
it "run commands in pipe line" do
|
36
|
+
expect(Hysh.out_s ->{ Hysh.pipe(["echo", "-n", "abc"], ["tr", "ab", "AB"]) }).to eql(["ABc", true])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/test/dpkg_test.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- ruby-indent-level: 2; -*-
|
2
|
+
|
3
|
+
require_relative "../lib/hysh"
|
4
|
+
|
5
|
+
def dpkg_installed1(package_names = nil)
|
6
|
+
Hysh.out_lines ->{
|
7
|
+
Hysh.pipe ['dpkg', '-l'],
|
8
|
+
if package_names
|
9
|
+
['egrep', "(#{package_names.join '|'})"]
|
10
|
+
else
|
11
|
+
['cat']
|
12
|
+
end
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def dpkg_installed2(package_names = nil)
|
17
|
+
Hysh.out_lines ->{
|
18
|
+
Hysh.pipe ['dpkg', '-l'] {
|
19
|
+
proc_line = if package_names
|
20
|
+
->l{
|
21
|
+
if package_names.any? { |pkg|
|
22
|
+
l.index pkg
|
23
|
+
}
|
24
|
+
l
|
25
|
+
end
|
26
|
+
}
|
27
|
+
else
|
28
|
+
->l{ l }
|
29
|
+
end
|
30
|
+
Hysh.filter_line &proc_line
|
31
|
+
}
|
32
|
+
}
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hysh
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Huang, Ying
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-09-12 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: IO redirection and command glue in Ruby
|
14
|
+
email: huang.ying.caritas@gmail.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- Gemfile
|
20
|
+
- README.rdoc
|
21
|
+
- Rakefile
|
22
|
+
- lib/hysh.rb
|
23
|
+
- spec/hysh_spec.rb
|
24
|
+
- test/dpkg_test.rb
|
25
|
+
homepage: http://github.com/hying-caritas/ruby-hysh
|
26
|
+
licenses:
|
27
|
+
- LGPL
|
28
|
+
metadata: {}
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
requirements: []
|
44
|
+
rubyforge_project:
|
45
|
+
rubygems_version: 2.2.2
|
46
|
+
signing_key:
|
47
|
+
specification_version: 4
|
48
|
+
summary: Huang Ying's SHell in Ruby
|
49
|
+
test_files: []
|
50
|
+
has_rdoc:
|