rexe 0.10.1 → 0.10.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +120 -47
- data/exe/rexe +6 -3
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bdd31e24705dfe69cc4f83f5529d1fd528401cb1a675e4203c037c92c4d88eae
|
4
|
+
data.tar.gz: '08e378d2729a9af62773deed3f4b653e702b97835e08ceb1b77622eda78d63be'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb5f6e5d5a8a19b18fd6ebdd6e5a12bdd6defe25638a91a9602697ba36057bbc905f1c0fb64ba30f830651bb825566eec455d94570f21751458e8c29ad371a0a
|
7
|
+
data.tar.gz: 676a94dfa2890c7711b6863dd9840c22cc402d09c940d4eace18832f78e254ff96fb1bf226e0f795c42da8c9f3c17f7630df68d54a7544bf54f8033a71cf2dfb
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -3,6 +3,9 @@ title: The `rexe` Command Line Executor and Filter
|
|
3
3
|
date: 2019-02-15
|
4
4
|
---
|
5
5
|
|
6
|
+
[Caution: This is a long article! If you lose patience reading it, I suggest skimming the headings
|
7
|
+
and the source code, and at minimum, reading the Conclusion.]
|
8
|
+
|
6
9
|
I love the power of the command line, but not the awkwardness of shell scripting
|
7
10
|
languages. Sure, there's a lot that can be done with them, but it doesn't take
|
8
11
|
long before I get frustrated with their bluntness and verbosity.
|
@@ -21,24 +24,31 @@ An excerpt of the output follows the code:
|
|
21
24
|
|
22
25
|
```
|
23
26
|
➜ ~ export EUR_RATES_JSON=`curl https://api.exchangeratesapi.io/latest`
|
24
|
-
➜ ~ echo $EUR_RATES_JSON | ruby -r json -r
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
}
|
27
|
+
➜ ~ echo $EUR_RATES_JSON | ruby -r json -r yaml -e 'puts JSON.parse(STDIN.read).to_yaml'
|
28
|
+
---
|
29
|
+
rates:
|
30
|
+
MXN: 21.96
|
31
|
+
AUD: 1.5964
|
32
|
+
HKD: 8.8092
|
33
|
+
...
|
34
|
+
base: EUR
|
35
|
+
date: '2019-03-08'
|
34
36
|
```
|
35
37
|
|
36
|
-
However, the configuration setup (the `require`s)
|
37
|
-
approach.
|
38
|
+
However, the configuration setup (the `require`s) along with the reading, parsing, and formatting
|
39
|
+
make the command long and tedious, discouraging this approach.
|
38
40
|
|
39
41
|
### Rexe
|
40
42
|
|
41
|
-
Enter the `rexe` script
|
43
|
+
Enter the `rexe` script: [^1]
|
44
|
+
|
45
|
+
Among other things, `rexe` provides switch-activated input parsing and output formatting so that converting
|
46
|
+
from one format to another is trivial.
|
47
|
+
This command does the same thing as the previous `ruby` command:
|
48
|
+
|
49
|
+
```
|
50
|
+
➜ ~ echo $EUR_RATES_JSON | rexe -mb -ij -oy self
|
51
|
+
```
|
42
52
|
|
43
53
|
`rexe` is at https://github.com/keithrbennett/rexe and can be installed with
|
44
54
|
`gem install rexe`. `rexe` provides several ways to simplify Ruby on the command
|
@@ -47,10 +57,12 @@ line, tipping the scale so that it is practical to do it more often.
|
|
47
57
|
Here is `rexe`'s help text as of the time of this writing:
|
48
58
|
|
49
59
|
```
|
50
|
-
rexe -- Ruby Command Line Executor/Filter -- v0.10.
|
60
|
+
rexe -- Ruby Command Line Executor/Filter -- v0.10.2 -- https://github.com/keithrbennett/rexe
|
51
61
|
|
52
62
|
Executes Ruby code on the command line, optionally taking standard input and writing to standard output.
|
53
63
|
|
64
|
+
rexe [options] 'Ruby source code'
|
65
|
+
|
54
66
|
Options:
|
55
67
|
|
56
68
|
-c --clear_options Clear all previous command line options specified up to now
|
@@ -65,7 +77,7 @@ Options:
|
|
65
77
|
-ml line mode; each line is ingested as a separate string
|
66
78
|
-me enumerator mode
|
67
79
|
-mb big string mode; all lines combined into single multiline string
|
68
|
-
-mn (default) no input mode; no special handling of input; self is
|
80
|
+
-mn (default) no input mode; no special handling of input; self is an Object.new
|
69
81
|
-n, --[no-]noop Do not execute the code (useful with -v); see note (1) below
|
70
82
|
-o, --output_format FORMAT Output format (puts is default):
|
71
83
|
-oi Inspect
|
@@ -76,7 +88,7 @@ Options:
|
|
76
88
|
-op Puts (default)
|
77
89
|
-os to_s
|
78
90
|
-oy YAML
|
79
|
-
-r, --require
|
91
|
+
-r, --require REQUIRE(S) Gems and built-in libraries to require, comma separated, or ! to clear
|
80
92
|
-v, --[no-]verbose verbose mode (logs to stderr); see note (1) below
|
81
93
|
|
82
94
|
If there is an .rexerc file in your home directory, it will be run as Ruby code
|
@@ -89,16 +101,6 @@ so that you can specify options implicitly (e.g. `export REXE_OPTIONS="-r awesom
|
|
89
101
|
-v no, -v yes, -v false, -v true, -v n, -v y, -v +, but not -v -
|
90
102
|
```
|
91
103
|
|
92
|
-
For consistency with the `ruby` interpreter we called previously, `rexe` supports requires with the `-r` option, but as one tiny improvement it also allows grouping them together using commas:
|
93
|
-
|
94
|
-
```
|
95
|
-
vvvvvvvvvvvvvvvvvvvvv
|
96
|
-
➜ ~ echo $EUR_RATES_JSON | rexe -r json,awesome_print 'ap JSON.parse(STDIN.read)'
|
97
|
-
^^^^^^^^^^^^^^^^^^^^^
|
98
|
-
```
|
99
|
-
|
100
|
-
This command produces the same results as the previous `ruby` one.
|
101
|
-
|
102
104
|
### Simplifying the Rexe Invocation with Configuration
|
103
105
|
|
104
106
|
`rexe` provides two approaches to configuration:
|
@@ -271,9 +273,12 @@ eybdoog
|
|
271
273
|
`reverse` is implicitly called on each line of standard input. `self`
|
272
274
|
is the input line in each call (we could also have used `self.reverse` but the `self` would have been redundant.).
|
273
275
|
|
274
|
-
Be aware that
|
275
|
-
is
|
276
|
-
|
276
|
+
Be aware that although you can control the _content_ of output records,
|
277
|
+
there is no way to selectively _exclude_ records from being output. Even if the result of the code
|
278
|
+
is nil or the empty string, a newline will be output. If this is an issue, you could do one of the following:
|
279
|
+
|
280
|
+
* use Enumerator mode and call `select`, `filter`, `reject`, etc.
|
281
|
+
* use the `-on` _no output_ mode and call `puts` explicitly for the output you _do_ want
|
277
282
|
|
278
283
|
|
279
284
|
#### -me "Enumerator" Filter Mode
|
@@ -299,23 +304,21 @@ Since `self` is an enumerable, we can call `first` and then `each_with_index`.
|
|
299
304
|
|
300
305
|
#### -mb "Big String" Filter Mode
|
301
306
|
|
302
|
-
In this mode, all standard input is combined into a single, (possibly
|
303
|
-
large string, with newline characters joining the lines in the string.
|
307
|
+
In this mode, all standard input is combined into a single, (possibly
|
308
|
+
large) string, with newline characters joining the lines in the string.
|
304
309
|
|
305
|
-
A good example of when you would use this is when you parse JSON or YAML text;
|
310
|
+
A good example of when you would use this is when you parse JSON or YAML text;
|
311
|
+
you need to pass the entire (probably) multiline string to the parse method.
|
312
|
+
This is the mode that was used in the first `rexe` example in this article.
|
306
313
|
|
307
|
-
An earlier example would be more simply specified using this mode, since `STDIN.read` could be replaced with `self`:
|
308
|
-
|
309
|
-
```
|
310
|
-
➜ ~ echo $EUR_RATES_JSON | rexe -mb -r awesome_print,json 'ap JSON.parse(self)'
|
311
|
-
```
|
312
314
|
|
313
315
|
#### -mn "No Input" Executor Mode -- The Default
|
314
316
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
317
|
+
In this mode, no special handling of standard input is done at all;
|
318
|
+
if you want standard input you need to code it yourself (e.g. with `STDIN.read`).
|
319
|
+
|
320
|
+
`self` evaluates to a new instance of `Object`, which would be used
|
321
|
+
if you defined methods, constants, instance variables, etc., in your code.
|
319
322
|
|
320
323
|
|
321
324
|
#### Filter Input Mode Memory Considerations
|
@@ -325,9 +328,9 @@ If you may have more input than would fit in memory, you can do the following:
|
|
325
328
|
* use `-ml` (line) mode so you are fed only 1 line at a time
|
326
329
|
* use an Enumerator, either by specifying the `-me` (enumerator) mode option,
|
327
330
|
or using `-mn` (no input) mode in conjunction with something like `STDIN.each_line`. Then:
|
328
|
-
|
331
|
+
* Make sure not to call any methods (e.g. `map`, `select`)
|
329
332
|
that will produce an array of all the input because that will pull all the records into memory, or:
|
330
|
-
|
333
|
+
* use [lazy enumerators](https://www.honeybadger.io/blog/using-lazy-enumerators-to-work-with-large-files-in-ruby/)
|
331
334
|
|
332
335
|
|
333
336
|
|
@@ -396,7 +399,8 @@ so calls to your custom methods _look_ like built in language commands and keywo
|
|
396
399
|
|
397
400
|
One complication of using utilities like `rexe` where Ruby code is specified on the shell command line is that
|
398
401
|
you need to be careful about the shell's special treatment of certain characters. For this reason, it is often
|
399
|
-
necessary to quote the Ruby code. You can use single or double quotes
|
402
|
+
necessary to quote the Ruby code. You can use single or double quotes to have the shell treat your source code
|
403
|
+
as a single argument.
|
400
404
|
An excellent reference for how they differ is [here](https://stackoverflow.com/questions/6697753/difference-between-single-and-double-quotes-in-bash).
|
401
405
|
|
402
406
|
Feel free to fall back on Ruby's super useful `%q{}` and `%Q{}`, equivalent to single and double quotes, respectively.
|
@@ -454,6 +458,70 @@ If I add `| pbcopy` to the rexe command, then that output text would be copied i
|
|
454
458
|
displayed in the terminal, and I could then paste it in my editor.
|
455
459
|
|
456
460
|
|
461
|
+
### Multiline Ruby Commands
|
462
|
+
|
463
|
+
Although `rexe` is cleanest with short one liners, you may want to use it to include nontrivial Ruby code
|
464
|
+
in your shell script as well. If you do this, you may need to:
|
465
|
+
|
466
|
+
* add trailing backslashes to lines of Ruby code
|
467
|
+
* use %q{} and %Q{} in your Ruby code instead of single and double quotes,
|
468
|
+
since the quotes have special meaning to the shell
|
469
|
+
|
470
|
+
|
471
|
+
### The Use of Semicolons
|
472
|
+
|
473
|
+
You will probably find yourself using semicolons much more often than usual when you use `rexe`.
|
474
|
+
Obviously you would need them to separate statements on the same line:
|
475
|
+
|
476
|
+
```
|
477
|
+
➜ ~ cowsay hello | rexe -me "print %Q{\u001b[33m}; puts to_a"
|
478
|
+
```
|
479
|
+
|
480
|
+
What might not be so obvious is that you _also_ need them if each statement is on its own line.
|
481
|
+
For example, here is an example without a semicolon:
|
482
|
+
|
483
|
+
```
|
484
|
+
➜ ~ cowsay hello | rexe -me "print %Q{\u001b[33m} \
|
485
|
+
puts to_a"
|
486
|
+
|
487
|
+
/Users/kbennett/.rvm/gems/ruby-2.6.0/gems/rexe-0.10.1/exe/rexe:256:in `eval':
|
488
|
+
(eval):1: syntax error, unexpected tIDENTIFIER, expecting '}' (SyntaxError)
|
489
|
+
...new { print %Q{\u001b[33m} puts to_a }
|
490
|
+
... ^~~~
|
491
|
+
```
|
492
|
+
|
493
|
+
The shell combines all backslash terminated lines into a single line of text, so when the Ruby
|
494
|
+
interpreter sees your code, it's all in a single line. Adding the semicolon fixes the problem:
|
495
|
+
|
496
|
+
```
|
497
|
+
➜ ~ cowsay hello | rexe -me "print %Q{\u001b[33m}; \
|
498
|
+
puts to_a"
|
499
|
+
|
500
|
+
eval_context_object: #<Enumerator:0x00007f92b1972840>
|
501
|
+
_______
|
502
|
+
< hello >
|
503
|
+
-------
|
504
|
+
\ ^__^
|
505
|
+
\ (oo)\_______
|
506
|
+
(__)\ )\/\
|
507
|
+
||----w |
|
508
|
+
|| ||
|
509
|
+
```
|
510
|
+
|
511
|
+
|
512
|
+
### Comma Separated Requires and Loads
|
513
|
+
|
514
|
+
For consistency with the `ruby` interpreter, `rexe` supports requires with the `-r` option, but
|
515
|
+
also allows grouping them together using commas:
|
516
|
+
|
517
|
+
```
|
518
|
+
vvvvvvvvvvvvvvvvvvvvv
|
519
|
+
➜ ~ echo $EUR_RATES_JSON | rexe -r json,awesome_print 'ap JSON.parse(STDIN.read)'
|
520
|
+
^^^^^^^^^^^^^^^^^^^^^
|
521
|
+
```
|
522
|
+
|
523
|
+
Files loaded with the `-l` option are treated the same way.
|
524
|
+
|
457
525
|
### More Examples
|
458
526
|
|
459
527
|
Here are some more examples to illustrate the use of `rexe`.
|
@@ -642,15 +710,20 @@ configuration from your command line so that you can focus on the high level
|
|
642
710
|
task at hand.
|
643
711
|
|
644
712
|
When we think of a new piece of software, we usually think "what would this be
|
645
|
-
helpful with now?". However, the power of `rexe` is not so much what can
|
646
|
-
with it in a single use case now, but rather what will
|
647
|
-
used to the concept and my supporting code and its uses evolve.
|
713
|
+
helpful with now?". However, for me, the power of `rexe` is not so much what I can do
|
714
|
+
with it in a single use case now, but rather what will I be able to do with it over time
|
715
|
+
as I get used to the concept and my supporting code and its uses evolve.
|
648
716
|
|
649
717
|
I suggest starting to use `rexe` even for modest improvements in workflow, even
|
650
718
|
if it doesn't seem compelling. There's a good chance that as you use it over
|
651
719
|
time, new ideas will come to you and the workflow improvements will increase
|
652
720
|
exponentially.
|
653
721
|
|
722
|
+
A word of caution though --
|
723
|
+
the complexity and difficulty of sharing your `rexe` scripts across systems
|
724
|
+
will be proportional to the extent to which you use environment variables
|
725
|
+
and loaded files for configuration and shared code.
|
726
|
+
Be responsible and disciplined in making this configuration as organized as possible.
|
654
727
|
|
655
728
|
#### Footnotes
|
656
729
|
|
data/exe/rexe
CHANGED
@@ -10,7 +10,7 @@ require 'shellwords'
|
|
10
10
|
|
11
11
|
class Rexe < Struct.new(:input_format, :input_mode, :loads, :output_format, :requires, :verbose, :noop)
|
12
12
|
|
13
|
-
VERSION = '0.10.
|
13
|
+
VERSION = '0.10.2'
|
14
14
|
|
15
15
|
def initialize
|
16
16
|
clear_options
|
@@ -19,6 +19,7 @@ class Rexe < Struct.new(:input_format, :input_mode, :loads, :output_format, :req
|
|
19
19
|
|
20
20
|
# Used as an initializer and also when `-!` is specified on the command line.
|
21
21
|
def clear_options
|
22
|
+
self.input_format = :none
|
22
23
|
self.input_mode = :no_input
|
23
24
|
self.output_format = :puts
|
24
25
|
self.loads = []
|
@@ -36,6 +37,8 @@ class Rexe < Struct.new(:input_format, :input_mode, :loads, :output_format, :req
|
|
36
37
|
|
37
38
|
Executes Ruby code on the command line, optionally taking standard input and writing to standard output.
|
38
39
|
|
40
|
+
rexe [options] 'Ruby source code'
|
41
|
+
|
39
42
|
Options:
|
40
43
|
|
41
44
|
-c --clear_options Clear all previous command line options specified up to now
|
@@ -50,7 +53,7 @@ class Rexe < Struct.new(:input_format, :input_mode, :loads, :output_format, :req
|
|
50
53
|
-ml line mode; each line is ingested as a separate string
|
51
54
|
-me enumerator mode
|
52
55
|
-mb big string mode; all lines combined into single multiline string
|
53
|
-
-mn (default) no input mode; no special handling of input; self is
|
56
|
+
-mn (default) no input mode; no special handling of input; self is an Object.new
|
54
57
|
-n, --[no-]noop Do not execute the code (useful with -v); see note (1) below
|
55
58
|
-o, --output_format FORMAT Output format (puts is default):
|
56
59
|
-oi Inspect
|
@@ -61,7 +64,7 @@ class Rexe < Struct.new(:input_format, :input_mode, :loads, :output_format, :req
|
|
61
64
|
-op Puts (default)
|
62
65
|
-os to_s
|
63
66
|
-oy YAML
|
64
|
-
-r, --require
|
67
|
+
-r, --require REQUIRE(S) Gems and built-in libraries to require, comma separated, or ! to clear
|
65
68
|
-v, --[no-]verbose verbose mode (logs to stderr); see note (1) below
|
66
69
|
|
67
70
|
If there is an .rexerc file in your home directory, it will be run as Ruby code
|