rexe 0.0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +161 -49
- data/exe/rexe +75 -31
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: facbd7a97a0fba70fb40170f61f53a127b6cb1ec05d44aed85a653a97b711d88
|
4
|
+
data.tar.gz: f742717a826d26f719485bf7cb562e598d1c3130ca3abe4065c2bd1bc90c34c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 819cf812598dfb9612310a71c4b9bb5c8fa00442e16650b9cb8441b2e21a36e39a9e1ead336ca4be9451edf26edf9599f14b1c75ac5935fd069535118f5d78c2
|
7
|
+
data.tar.gz: fe8998aab47ae22b8082439305b28f719296117f183cf49e632ecfe6192cb02bcc99a2536ba9b7b37361642c27619c0cff7d92bae14cd5a8f2a0017369aaab6d
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
## rexe -- Ruby Command Line Executor
|
2
2
|
|
3
|
+
|
4
|
+
### v0.2.0
|
5
|
+
|
6
|
+
* Improve README and verbose logging.
|
7
|
+
|
8
|
+
### v0.1.0
|
9
|
+
|
10
|
+
* Add ability to handle input as a single multiline string (using -mb option).
|
11
|
+
* Add -mn mode for no input at all.
|
12
|
+
* Fix and improve usage examples in README.
|
13
|
+
|
14
|
+
|
15
|
+
### v0.0.2
|
16
|
+
|
17
|
+
* Fix running-as-script test.
|
18
|
+
|
19
|
+
|
3
20
|
### v0.0.1
|
4
21
|
|
5
22
|
* Initial version.
|
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# Rexe
|
2
2
|
|
3
|
+
A configurable Ruby command line filter/executor.
|
4
|
+
|
5
|
+
|
3
6
|
|
4
7
|
## Installation
|
5
8
|
|
@@ -16,85 +19,194 @@ chmod +x rexe
|
|
16
19
|
|
17
20
|
## Usage
|
18
21
|
|
22
|
+
rexe is a _filter_ in that it can consume standard input and emit standard output; but it is also an _executor_, meaning it can be used without either standard input or output.
|
23
|
+
|
24
|
+
### Help Text
|
25
|
+
|
26
|
+
As a summary, here is the help text printed out by the application:
|
27
|
+
|
19
28
|
```
|
20
29
|
|
21
|
-
rexe -- Ruby Command Line Filter -- v0.0.1 -- https://github.com/keithrbennett/rexe
|
22
30
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
call chomp on the lines yourself to remove the trailing newlines.
|
31
|
+
rexe -- Ruby Command Line Filter -- v1.1.0 -- https://github.com/keithrbennett/rexe
|
32
|
+
|
33
|
+
Optionally takes standard input and runs the specified code on it, sending the result to standard output.
|
27
34
|
|
28
35
|
Options:
|
29
36
|
|
30
37
|
-h, --help Print help and exit
|
31
|
-
-
|
38
|
+
-l, --load A_RUBY_FILE Load this Ruby source code file
|
39
|
+
-m, --mode MODE Mode with which to handle input (i.e. what `self` will be in the code):
|
40
|
+
-ms for each line to be handled separately as a string (default)
|
41
|
+
-me for an enumerator of lines (least memory consumption for big data)
|
42
|
+
-mb for 1 big string (all lines combined into single multiline string)
|
43
|
+
-mn to execute the specified Ruby code on no input at all
|
32
44
|
-r, --require REQUIRES Gems and built-in libraries (e.g. shellwords, yaml) to require, comma separated
|
33
45
|
-v, --[no-]verbose Verbose mode, writes to stderr
|
34
46
|
|
35
|
-
If there is an .rexerc file in your home directory, it will be run as Ruby code
|
47
|
+
If there is an .rexerc file in your home directory, it will be run as Ruby code
|
48
|
+
before processing the input.
|
36
49
|
|
37
50
|
If there is an REXE_OPTIONS environment variable, its content will be prepended to the command line
|
38
51
|
so that you can specify options implicitly (e.g. `export REXE_OPTIONS="-r awesome_print,yaml"`)
|
39
52
|
|
40
53
|
```
|
41
|
-
## License
|
42
54
|
|
43
|
-
|
55
|
+
### Input Mode
|
56
|
+
|
57
|
+
When it is used as a filter, the input is accessed differently in the source code depending on the mode that was specified (see example section below for examples). The mode letter is appended to `-m` on the command line; `s` (_string_) mode is the default.
|
58
|
+
|
59
|
+
* `s` - _string_ mode - the source code is run once on each line of input, and `self` is each line of text
|
60
|
+
* `e` - _enumerator_ mode - the code is run once on the enumerator of all lines; `self` is the enumerator, so you can call `map`, `to_a`, `select`, etc without explicitly specifying `self`.
|
61
|
+
* `b` - _big string_ mode - all input is treated as one large (probably) multiline string with the newlines intact; `self` is this large string; this mode is required, for example, for parsing the text into a single object from JSON or YAML formatted data.
|
62
|
+
* `n` - _no input_ mode - this instructs the program to proceed without looking for input
|
63
|
+
|
64
|
+
|
65
|
+
### Requires
|
66
|
+
|
67
|
+
As with the Ruby interpreter itself, `require`s can be specified on the command line with the `-r` option. Multiple requires can be combined with commas between them.
|
68
|
+
|
69
|
+
|
70
|
+
### Loading Ruby Files
|
71
|
+
|
72
|
+
Other Ruby files can be loaded by `rexe`, to, for example, define classes and methods to use, set up resources, etc. They are specified with the `-l` option.
|
73
|
+
|
74
|
+
If there is a file named `.rexerc` in the home directory, that will always be loaded without explicitly requesting it on the command line.
|
75
|
+
|
76
|
+
|
77
|
+
### Verbose Mode
|
78
|
+
|
79
|
+
Verbose mode outputs information to standard error (stderr). This information can be redirected, for example to a file named `rexe.log`, by adding `2>& rexe.log` to the command line.
|
80
|
+
|
81
|
+
Here is an example of some text that might be output in verbose mode:
|
82
|
+
|
83
|
+
```
|
84
|
+
rexe version 1.1.0 -- 2019-02-04 21:50:34 +0700
|
85
|
+
Source Code: sort.to_a.first(3).ai
|
86
|
+
Requiring awesome_print
|
87
|
+
Loading global config file /Users/kbennett/.rexerc
|
88
|
+
```
|
89
|
+
|
90
|
+
### The REXE_OPTIONS Environment Variable
|
91
|
+
|
92
|
+
Very often you will want to call `rexe` several times with similar options. Instead of having to clutter the command line each time with these options, you can put them in an environment variable named `REXE_OPTIONS`, and they will be prepended automatically. Since they will be processed before the options on the command line, they are of lower precedence and can be overridden.
|
93
|
+
|
44
94
|
|
95
|
+
## Troubleshooting
|
96
|
+
|
97
|
+
One common problem relates to the shell's special handling of characters. Remember that the shell will process special characters, thereby changing your text before passing it on to the Ruby code. It is good to get in the habit of putting your source code in double quotes; and if the source code itself uses quotes, use `q{}` or `Q{}` instead. For example:
|
98
|
+
|
99
|
+
```
|
100
|
+
➜ rexe -mn "puts %Q{The time is now #{Time.now}}"
|
101
|
+
The time is now 2019-02-04 18:49:31 +0700
|
102
|
+
```
|
103
|
+
|
104
|
+
If you are troubleshooting the setup (i.e. the command line options, loaded files, and `REXE_OPTIONS` environment variable) using the verbose option, you may have the problem of the logging scrolling off the screen due to the length of your output. In this case you could easily fake or disable the output adding `; nil`, `; 'foo'`', etc. to the end of your expression. This way you don't have to mess with your code's logic.
|
45
105
|
|
46
106
|
## Examples
|
47
107
|
|
48
108
|
```
|
49
|
-
|
50
|
-
|
109
|
+
# Call reverse on listed file.
|
110
|
+
# No need to specify the mode, since it defaults to "s" ("-ms"),
|
111
|
+
# which treats every line separately.
|
112
|
+
➜ rexe git:(master) ✗ ls | head -2 | exe/rexe "self + ' --> ' + reverse"
|
113
|
+
CHANGELOG.md --> dm.GOLEGNAHC
|
114
|
+
Gemfile --> elifmeG
|
51
115
|
|
52
|
-
|
53
|
-
20:51
|
116
|
+
----
|
54
117
|
|
118
|
+
# Use input data to create a human friendly message:
|
119
|
+
➜ ~ uptime | rexe "%Q{System has been up: #{split.first}.}"
|
120
|
+
System has been up: 17:10.
|
55
121
|
|
56
|
-
|
57
|
-
["Gemfile\n","LICENSE.txt\n","README.md\n","Rakefile\n","bin\n","exe\n","lib\n","rexe.gemspec\n","spec\n"]
|
122
|
+
----
|
58
123
|
|
124
|
+
# Create a JSON array of a file listing.
|
125
|
+
# Use the "-me" flag so that all input is treated as a single enumerator of lines.
|
126
|
+
➜ /etc ls | head -3 | rexe -me -r json "map(&:strip).to_a.to_json"
|
127
|
+
["AFP.conf","afpovertcp.cfg","afpovertcp.cfg~orig"]
|
59
128
|
|
60
|
-
|
61
|
-
---
|
62
|
-
- Gemfile
|
63
|
-
- LICENSE.txt
|
64
|
-
- README.md
|
65
|
-
- Rakefile
|
66
|
-
- bin
|
67
|
-
- exe
|
68
|
-
- lib
|
69
|
-
- rexe.gemspec
|
70
|
-
- spec
|
129
|
+
----
|
71
130
|
|
131
|
+
# Create a "pretty" JSON array of a file listing:
|
132
|
+
➜ /etc ls | head -3 | rexe -me -r json "JSON.pretty_generate(map(&:strip).to_a)"
|
133
|
+
[
|
134
|
+
"AFP.conf",
|
135
|
+
"afpovertcp.cfg",
|
136
|
+
"afpovertcp.cfg~orig"
|
137
|
+
]
|
72
138
|
|
73
|
-
|
139
|
+
----
|
74
140
|
|
75
|
-
|
141
|
+
# Create a YAML array of a file listing:
|
142
|
+
➜ /etc ls | head -3 | rexe -me -r yaml "map(&:strip).to_a.to_yaml"
|
76
143
|
---
|
77
|
-
-
|
78
|
-
-
|
79
|
-
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
➜ rexe git:(master) ✗ ls | exe/rexe "map(&:chomp).to_a.ai"
|
144
|
+
- AFP.conf
|
145
|
+
- afpovertcp.cfg
|
146
|
+
- afpovertcp.cfg~orig
|
147
|
+
|
148
|
+
----
|
149
|
+
|
150
|
+
# Use AwesomePrint to print a file listing.
|
151
|
+
# (Rather than calling the `ap` method on the object to print,
|
152
|
+
# call the `ai` method _on_ the object to print:
|
153
|
+
➜ /etc ls | head -3 | rexe -me -r awesome_print "map(&:chomp).ai"
|
88
154
|
[
|
89
|
-
[0] "
|
90
|
-
[1] "
|
91
|
-
[2] "
|
92
|
-
[3] "Rakefile",
|
93
|
-
[4] "bin",
|
94
|
-
[5] "exe",
|
95
|
-
[6] "lib",
|
96
|
-
[7] "rexe.gemspec",
|
97
|
-
[8] "spec"
|
155
|
+
[0] "AFP.conf",
|
156
|
+
[1] "afpovertcp.cfg",
|
157
|
+
[2] "afpovertcp.cfg~orig"
|
98
158
|
]
|
99
159
|
|
100
|
-
|
160
|
+
----
|
161
|
+
|
162
|
+
# Don't use input at all, so use "-mn" to tell rexe not to expect input.
|
163
|
+
➜ /etc rexe -mn "%Q{The time is now #{Time.now}}"
|
164
|
+
The time is now 2019-02-04 17:20:03 +0700
|
165
|
+
|
166
|
+
----
|
167
|
+
|
168
|
+
# Use REXE_OPTIONS environment variable to eliminate the need to specify
|
169
|
+
# options on each invocation:
|
170
|
+
|
171
|
+
# First it will fail since these symbols have not been loaded via require:
|
172
|
+
➜ /etc rexe -mn "[JSON, YAML, AwesomePrint]"
|
173
|
+
Traceback (most recent call last):
|
174
|
+
...
|
175
|
+
(eval):1:in `block in call': uninitialized constant Rexe::JSON (NameError)
|
176
|
+
|
177
|
+
# Now we specify the requires in the REXE_OPTIONS environment variable.
|
178
|
+
# Contents of this variable will be prepended to the arguments
|
179
|
+
# specified on the command line.
|
180
|
+
➜ /etc export REXE_OPTIONS="-r json,yaml,awesome_print"
|
181
|
+
|
182
|
+
# Now that command that previously failed will succeed:
|
183
|
+
➜ /etc rexe -mn "[JSON, YAML, AwesomePrint].to_s"
|
184
|
+
[JSON, Psych, AwesomePrint]
|
185
|
+
|
186
|
+
----
|
187
|
+
|
188
|
+
Access public JSON data and print it with awesome_print:
|
189
|
+
|
190
|
+
➜ /etc curl https://data.lacity.org/api/views/nxs9-385f/rows.json\?accessType\=DOWNLOAD \
|
191
|
+
| rexe -mb -r awesome_print,json "JSON.parse(self).ai"
|
192
|
+
{
|
193
|
+
"meta" => {
|
194
|
+
"view" => {
|
195
|
+
"id" => "nxs9-385f",
|
196
|
+
"name" => "2010 Census Populations by Zip Code",
|
197
|
+
...
|
198
|
+
|
199
|
+
----
|
200
|
+
|
201
|
+
# Print the environment variables, sorted, with Awesome Print:
|
202
|
+
➜ /etc env | rexe -me -r awesome_print sort.to_a.ai
|
203
|
+
[
|
204
|
+
...
|
205
|
+
[ 4] "COLORFGBG=15;0\n",
|
206
|
+
[ 5] "COLORTERM=truecolor\n",
|
207
|
+
...
|
208
|
+
```
|
209
|
+
|
210
|
+
## License
|
211
|
+
|
212
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/exe/rexe
CHANGED
@@ -1,20 +1,21 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
#
|
3
|
-
# rexe - Ruby Command Line Filter
|
3
|
+
# rexe - Ruby Command Line Executor Filter
|
4
4
|
#
|
5
5
|
# Inspired by https://github.com/thisredone/rb
|
6
|
-
|
6
|
+
|
7
7
|
|
8
8
|
require 'optparse'
|
9
9
|
require 'shellwords'
|
10
10
|
|
11
|
+
# Rexe - Ruby Executor
|
12
|
+
class Rexe < Struct.new(:input_mode, :loads, :requires, :verbose)
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
VERSION = '0.0.1'
|
14
|
+
VERSION = '0.2.0'
|
15
15
|
|
16
16
|
def initialize
|
17
|
-
self.
|
17
|
+
self.input_mode = :string
|
18
|
+
self.loads = []
|
18
19
|
self.requires = []
|
19
20
|
self.verbose = false
|
20
21
|
end
|
@@ -25,19 +26,22 @@ class Rexe < Struct.new(:line_mode, :requires, :verbose)
|
|
25
26
|
|
26
27
|
rexe -- Ruby Command Line Filter -- v#{VERSION} -- https://github.com/keithrbennett/rexe
|
27
28
|
|
28
|
-
|
29
|
-
Your Ruby code can operate on each line individually (-ms) (the default),
|
30
|
-
or operate on the enumerator of all lines (-me). If the latter, you will probably need to
|
31
|
-
call chomp on the lines yourself to remove the trailing newlines.
|
29
|
+
Optionally takes standard input and runs the specified code on it, sending the result to standard output.
|
32
30
|
|
33
31
|
Options:
|
34
32
|
|
35
33
|
-h, --help Print help and exit
|
36
|
-
-
|
34
|
+
-l, --load A_RUBY_FILE Load this Ruby source code file
|
35
|
+
-m, --mode MODE Mode with which to handle input (i.e. what `self` will be in the code):
|
36
|
+
-ms for each line to be handled separately as a string (default)
|
37
|
+
-me for an enumerator of lines (least memory consumption for big data)
|
38
|
+
-mb for 1 big string (all lines combined into single multiline string)
|
39
|
+
-mn to execute the specified Ruby code on no input at all
|
37
40
|
-r, --require REQUIRES Gems and built-in libraries (e.g. shellwords, yaml) to require, comma separated
|
38
41
|
-v, --[no-]verbose Verbose mode, writes to stderr
|
39
42
|
|
40
|
-
If there is an .rexerc file in your home directory, it will be run as Ruby code
|
43
|
+
If there is an .rexerc file in your home directory, it will be run as Ruby code
|
44
|
+
before processing the input.
|
41
45
|
|
42
46
|
If there is an REXE_OPTIONS environment variable, its content will be prepended to the command line
|
43
47
|
so that you can specify options implicitly (e.g. `export REXE_OPTIONS="-r awesome_print,yaml"`)
|
@@ -65,17 +69,25 @@ class Rexe < Struct.new(:line_mode, :requires, :verbose)
|
|
65
69
|
exit
|
66
70
|
end
|
67
71
|
|
72
|
+
parser.on('-l', '--load RUBY_FILE', 'Loads and runs this Ruby file') do |v|
|
73
|
+
self.loads << v
|
74
|
+
end
|
75
|
+
|
68
76
|
parser.on('-m', '--mode MODE',
|
69
77
|
'Mode with which to handle input (-ms for string (default), -me for enumerator)') do |v|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
78
|
+
|
79
|
+
modes = {
|
80
|
+
's' => :string,
|
81
|
+
'e' => :enumerator,
|
82
|
+
'b' => :one_big_string,
|
83
|
+
'n' => :no_input
|
84
|
+
}
|
85
|
+
|
86
|
+
self.input_mode = modes[v]
|
87
|
+
if self.input_mode.nil?
|
88
|
+
puts help_text
|
89
|
+
raise "Input mode must be one of #{modes.keys}."
|
90
|
+
end
|
79
91
|
end
|
80
92
|
|
81
93
|
parser.on('-r', '--require REQUIRES', 'Gems and modules to require, comma separated') do |v|
|
@@ -92,13 +104,20 @@ class Rexe < Struct.new(:line_mode, :requires, :verbose)
|
|
92
104
|
def load_global_config_if_exists
|
93
105
|
filespec = File.join(Dir.home, '.rexerc')
|
94
106
|
exists = File.exists?(filespec)
|
95
|
-
|
107
|
+
if exists
|
108
|
+
log_if_verbose("Loading global config file #{filespec}")
|
109
|
+
load(filespec)
|
110
|
+
end
|
96
111
|
exists ? filespec : nil
|
97
112
|
end
|
98
113
|
|
99
114
|
|
100
|
-
def execute(
|
101
|
-
|
115
|
+
def execute(eval_context_object, code)
|
116
|
+
if eval_context_object
|
117
|
+
puts eval_context_object.instance_eval(&code)
|
118
|
+
else
|
119
|
+
puts code.call
|
120
|
+
end
|
102
121
|
rescue Errno::EPIPE
|
103
122
|
exit(-13)
|
104
123
|
end
|
@@ -110,21 +129,46 @@ class Rexe < Struct.new(:line_mode, :requires, :verbose)
|
|
110
129
|
|
111
130
|
|
112
131
|
def call
|
132
|
+
start_time = Time.now
|
133
|
+
|
113
134
|
parse_command_line
|
114
135
|
|
115
|
-
log_if_verbose("
|
116
|
-
|
136
|
+
log_if_verbose("rexe version #{VERSION} -- #{Time.now}")
|
137
|
+
log_if_verbose('Source Code: ' + ARGV.join(' '))
|
117
138
|
|
118
|
-
|
119
|
-
|
139
|
+
requires.each do |r|
|
140
|
+
log_if_verbose("Requiring #{r}")
|
141
|
+
require(r)
|
142
|
+
end
|
143
|
+
|
144
|
+
load_global_config_if_exists
|
145
|
+
|
146
|
+
loads.each do |file|
|
147
|
+
log_if_verbose("Loading #{file}")
|
148
|
+
load(file)
|
149
|
+
end
|
120
150
|
|
121
151
|
source_code = "Proc.new { #{ARGV.join(' ')} }"
|
122
|
-
log_if_verbose("Source code: #{source_code}")
|
123
152
|
code = eval(source_code)
|
124
153
|
|
125
|
-
|
154
|
+
|
155
|
+
actions = {
|
156
|
+
string: -> { STDIN.each { |l| execute(l.chomp, code) } },
|
157
|
+
enumerator: -> { execute(STDIN.each_line, code) },
|
158
|
+
one_big_string: -> { big_string = STDIN.each_line.to_a.join; execute(big_string, code) },
|
159
|
+
no_input: -> { execute(nil, code) }
|
160
|
+
}
|
161
|
+
|
162
|
+
actions[input_mode].()
|
163
|
+
|
164
|
+
duration = Time.now - start_time
|
165
|
+
log_if_verbose("rexe time elapsed: #{duration} seconds.")
|
126
166
|
end
|
127
167
|
end
|
128
168
|
|
129
|
-
|
169
|
+
# This is needed because the gemspec file loads this file to access Rexe::VERSION
|
170
|
+
# and must not have it run at that time:
|
171
|
+
called_as_script = (File.basename($0) == File.basename(__FILE__))
|
172
|
+
Rexe.new.call if called_as_script
|
173
|
+
|
130
174
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rexe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Keith Bennett
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-02-
|
11
|
+
date: 2019-02-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -97,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
97
|
- !ruby/object:Gem::Version
|
98
98
|
version: '0'
|
99
99
|
requirements: []
|
100
|
-
rubygems_version: 3.0.
|
100
|
+
rubygems_version: 3.0.2
|
101
101
|
signing_key:
|
102
102
|
specification_version: 4
|
103
103
|
summary: Ruby Command Line Executor
|