rexe 0.14.0 → 0.15.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 +5 -0
- data/README.md +206 -105
- data/exe/rexe +40 -30
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a85b226f2d7d337d03476286bbe79f7b594519f93370198f8ae0581cec3616c
|
4
|
+
data.tar.gz: 77e944cb23215f4ed522e5c13b8f4457f0cbefcac67b26da8218d700d729a079
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c88dd6b11138b9339f73439687a847332957cb50b66a1c7bde4e38d6fbc4cc4c9931e0e40111054d9b0e784ec7ac0201e172d6a1224b4aa72b5fde8593087f6
|
7
|
+
data.tar.gz: 30e2902d826c9f32b12a2508152039752bd0c0f9738aa792fbadf133f57a19af28b0f8ea0ebc8ea85a052678ec8a748b5b1974ef4ffe811243b33cd2b20abd4d
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -3,8 +3,10 @@ title: The `rexe` Command Line Executor and Filter
|
|
3
3
|
date: 2019-02-15
|
4
4
|
---
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
[Caution: This is a long article!
|
7
|
+
You might want to skim the section headings and source code first.
|
8
|
+
Many sections can be skipped without sacrificing comprehensibility of the rest of them.
|
9
|
+
]
|
8
10
|
|
9
11
|
----
|
10
12
|
|
@@ -42,7 +44,7 @@ make the command long and tedious, discouraging this approach.
|
|
42
44
|
|
43
45
|
### Rexe
|
44
46
|
|
45
|
-
The `rexe` script [^1] can simplify such commands.
|
47
|
+
The `rexe` script [see footnote ^1] can simplify such commands.
|
46
48
|
Among other things, `rexe` provides switch-activated input parsing and output formatting so that converting
|
47
49
|
from one format to another is trivial.
|
48
50
|
The previous `ruby` command can be expressed in `rexe` as:
|
@@ -66,7 +68,7 @@ line, tipping the scale so that it is practical to do it more often.
|
|
66
68
|
Here is `rexe`'s help text as of the time of this writing:
|
67
69
|
|
68
70
|
```
|
69
|
-
rexe -- Ruby Command Line Executor/Filter -- v0.
|
71
|
+
rexe -- Ruby Command Line Executor/Filter -- v0.15.0 -- https://github.com/keithrbennett/rexe
|
70
72
|
|
71
73
|
Executes Ruby code on the command line, optionally automating management of standard input
|
72
74
|
and standard output, and optionally parsing input and formatting output with YAML, JSON, etc.
|
@@ -106,6 +108,9 @@ Options:
|
|
106
108
|
-r, --require REQUIRE(S) Gems and built-in libraries to require, comma separated;
|
107
109
|
! to clear all, or precede a name with '-' to remove
|
108
110
|
|
111
|
+
If source code is not specified, it will default to 'self', which is most likely useful
|
112
|
+
only in a filter mode (-ml, -me, -mb).
|
113
|
+
|
109
114
|
If there is an .rexerc file in your home directory, it will be run as Ruby code
|
110
115
|
before processing the input.
|
111
116
|
|
@@ -160,7 +165,7 @@ def valkyries
|
|
160
165
|
end
|
161
166
|
```
|
162
167
|
|
163
|
-
To digress a bit, why would you want this? You might want to be able to go to another room until a long job completes, and be notified when it is done. The `valkyries` method will launch a browser window pointed to Richard Wagner's "Ride of the Valkyries" starting at a lively point in the music. (The `open` command is Mac specific and could be replaced with `start` on Windows, a browser command name, etc.) [^2]
|
168
|
+
To digress a bit, why would you want this? You might want to be able to go to another room until a long job completes, and be notified when it is done. The `valkyries` method will launch a browser window pointed to Richard Wagner's "Ride of the Valkyries" starting at a lively point in the music. (The `open` command is Mac specific and could be replaced with `start` on Windows, a browser command name, etc.) [see footnote ^2]
|
164
169
|
|
165
170
|
If you like this kind of audio notification, you could download public domain audio files and use a command like player like `afplay` on Mac OS, or `mpg123` or `ogg123` on Linux. This approach is lighter weight, requires no network access, and will not leave an open browser window for you to close.
|
166
171
|
|
@@ -206,12 +211,13 @@ One of the examples at the end of this articles shows how you could have differe
|
|
206
211
|
### Logging
|
207
212
|
|
208
213
|
A log entry is optionally output to standard error after completion of the code.
|
209
|
-
|
214
|
+
This entry is a hash representation (to be precise, `to_h`) of the `$RC` OpenStruct described in the $RC section below.
|
215
|
+
It contains the version, date/time of execution, source code
|
210
216
|
to be evaluated, options (after parsing both the `REXE_OPTIONS` environment variable and the command line),
|
211
217
|
and the execution time of your Ruby code:
|
212
218
|
|
213
219
|
```
|
214
|
-
➜ ~ echo $EUR_RATES_JSON | rexe -gy -ij -oa self
|
220
|
+
➜ ~ echo $EUR_RATES_JSON | rexe -gy -ij -mb -oa self
|
215
221
|
...
|
216
222
|
---
|
217
223
|
:count: 0
|
@@ -268,9 +274,7 @@ to your code as `self`.
|
|
268
274
|
|
269
275
|
The last (and default) is the _executor_ mode. It merely assists you in
|
270
276
|
executing the code you provide without any special implicit handling of standard input.
|
271
|
-
|
272
|
-
All input modes automatically output to standard output the last value evaluated by your code.
|
273
|
-
You can suppress automatic output with the `-on` option.
|
277
|
+
Here is more detail on these modes:
|
274
278
|
|
275
279
|
|
276
280
|
#### -ml "Line" Filter Mode
|
@@ -279,26 +283,29 @@ In this mode, your code would be called once per line of input,
|
|
279
283
|
and in each call, `self` would evaluate to each line of text:
|
280
284
|
|
281
285
|
```
|
282
|
-
➜ ~ echo "hello\ngoodbye" | rexe -
|
286
|
+
➜ ~ echo "hello\ngoodbye" | rexe -ml puts reverse
|
283
287
|
olleh
|
284
288
|
eybdoog
|
285
289
|
```
|
286
290
|
|
287
291
|
`reverse` is implicitly called on each line of standard input. `self`
|
288
|
-
is the input line in each call (we could also have used `self.reverse` but the `self.` would have been redundant
|
292
|
+
is the input line in each call (we could also have used `self.reverse` but the `self.` would have been redundant).
|
289
293
|
|
290
|
-
Be aware that, in this mode,
|
294
|
+
Be aware that, in this mode, if you are using an automatic output mode
|
295
|
+
(anything other than the default `-on` no output mode),
|
296
|
+
although you can control the _content_ of output records,
|
291
297
|
there is no way to selectively _exclude_ records from being output. Even if the result of the code
|
292
298
|
is nil or the empty string, a newline will be output. To prevent this, you can do one of the following:
|
293
299
|
|
294
|
-
* use `-me` Enumerator mode and call `select`, `filter`, `reject`, etc.
|
295
|
-
* use the `-on` _no output_ mode and call `puts` explicitly for the output you _do_ want
|
300
|
+
* use `-me` Enumerator mode instead and call `select`, `filter`, `reject`, etc.
|
301
|
+
* use the (default) `-on` _no output_ mode and call `puts` explicitly for the output you _do_ want
|
296
302
|
|
297
303
|
|
298
304
|
#### -me "Enumerator" Filter Mode
|
299
305
|
|
300
306
|
In this mode, your code is called only once, and `self` is an enumerator
|
301
|
-
dispensing all lines of standard input. To be more precise, it is the enumerator returned by `
|
307
|
+
dispensing all lines of standard input. To be more precise, it is the enumerator returned by the `each_line` method,
|
308
|
+
on `$stdin` or the input file, whichever is applicable.
|
302
309
|
|
303
310
|
Dealing with input as an enumerator enables you to use the wealth of `Enumerable` methods such as `select`, `to_a`, `map`, etc.
|
304
311
|
|
@@ -306,7 +313,7 @@ Here is an example of using `-me` to add line numbers to the first 3
|
|
306
313
|
files in the directory listing:
|
307
314
|
|
308
315
|
```
|
309
|
-
➜ ~ ls / | rexe -me
|
316
|
+
➜ ~ ls / | rexe -me "first(3).each_with_index { |ln,i| puts '%5d %s' % [i, ln] }"
|
310
317
|
|
311
318
|
0 AndroidStudioProjects
|
312
319
|
1 Applications
|
@@ -314,7 +321,8 @@ files in the directory listing:
|
|
314
321
|
```
|
315
322
|
|
316
323
|
Since `self` is an enumerable, we can call `first` and then `each_with_index`.
|
317
|
-
|
324
|
+
We've used the default output mode `-on` (_no output_ mode), which says don't do any automatic output,
|
325
|
+
just the output explicitly specified by `puts` in the source code.
|
318
326
|
|
319
327
|
|
320
328
|
#### -mb "Big String" Filter Mode
|
@@ -338,7 +346,8 @@ if you defined methods, constants, instance variables, etc., in your code.
|
|
338
346
|
|
339
347
|
#### Filter Input Mode Memory Considerations
|
340
348
|
|
341
|
-
If you may have more input than would fit in memory,
|
349
|
+
If you are using one of the filter modes, and may have more input than would fit in memory,
|
350
|
+
you can do one of the following:
|
342
351
|
|
343
352
|
* use `-ml` (line) mode so you are fed only 1 line at a time
|
344
353
|
* use an Enumerator, either by a) specifying the `-me` (enumerator) mode option,
|
@@ -373,7 +382,7 @@ Several output formats are provided for your convenience:
|
|
373
382
|
* `-oi` - Inspect - calls `inspect` on the object
|
374
383
|
* `-oj` - JSON - calls `to_json` on the object
|
375
384
|
* `-oJ` - Pretty JSON calls `JSON.pretty_generate` with the object
|
376
|
-
* `-on` - No Output - output is suppressed
|
385
|
+
* `-on` - (default) No Output - output is suppressed
|
377
386
|
* `-op` - Puts - produces what `puts` would output
|
378
387
|
* `-os` - To String - calls `to_s` on the object
|
379
388
|
* `-oy` - YAML - calls `to_yaml` on the object
|
@@ -384,14 +393,14 @@ You may wonder why these formats are provided, given that their functionality
|
|
384
393
|
could be included in the custom code instead. Here's why:
|
385
394
|
|
386
395
|
* The savings in command line length goes a long way to making these commands more readable and feasible.
|
387
|
-
* It's much simpler to switch formats, as there is no need to change the code itself.
|
388
|
-
parameterization of the output format.
|
396
|
+
* It's much simpler to switch formats, as there is no need to change the code itself.
|
397
|
+
* This approach enables parameterization of the output format.
|
389
398
|
|
390
399
|
|
391
400
|
### Reading Input from a File
|
392
401
|
|
393
402
|
`rexe` also simplifies getting input from a file rather than standard input. The `-f` option takes a filespec
|
394
|
-
and does with exactly what it would have done with standard input. This shortens:
|
403
|
+
and does with its content exactly what it would have done with standard input. This shortens:
|
395
404
|
|
396
405
|
```
|
397
406
|
➜ ~ cat filename.ext | rexe ...
|
@@ -424,6 +433,29 @@ So the example we gave above:
|
|
424
433
|
Another possible win for using `-f` is that since it is a command line option, it could be specified in `REXE_OPTIONS`.
|
425
434
|
This could be useful if you are doing many operations on the same file.
|
426
435
|
|
436
|
+
|
437
|
+
### 'self' as Default Source Code
|
438
|
+
|
439
|
+
To make `rexe` even more concise, you do not need to specify any source code when you want that source code
|
440
|
+
to be `self`. This would be the case for simple format conversions, such as the following JSON to YAML conversion
|
441
|
+
of the currency coversion rates:
|
442
|
+
|
443
|
+
```
|
444
|
+
➜ ~ rexe -f eur_rates.json -oy
|
445
|
+
# or
|
446
|
+
➜ ~ echo $EUR_RATES_JSON | rexe -mb -ij -oy
|
447
|
+
|
448
|
+
---
|
449
|
+
rates:
|
450
|
+
JPY: 126.63
|
451
|
+
BRL: 4.3012
|
452
|
+
NOK: 9.6915
|
453
|
+
...
|
454
|
+
```
|
455
|
+
|
456
|
+
This feature is probably only useful in the filter modes, since in the executor mode (`-mn`) self is a new
|
457
|
+
instance of `Object` and hardly ever useful as an output value.
|
458
|
+
|
427
459
|
### The $RC Global OpenStruct
|
428
460
|
|
429
461
|
For your convenience, the information displayed in verbose mode is available to your code at runtime
|
@@ -450,7 +482,7 @@ OpenStruct {
|
|
450
482
|
}
|
451
483
|
```
|
452
484
|
|
453
|
-
Probably most useful in that object
|
485
|
+
Probably most useful in that object at runtime
|
454
486
|
is the record count, accessible with both `$RC.count` and `$RC.i`.
|
455
487
|
This is only really useful in line mode, because in the others
|
456
488
|
it will always be 0 or 1. Here is an example of how you might use it as a kind of progress indicator:
|
@@ -472,10 +504,15 @@ and removed by the shell.
|
|
472
504
|
|
473
505
|
### Implementing Domain Specific Languages (DSL's)
|
474
506
|
|
475
|
-
Defining methods in your loaded files enables you to effectively define a
|
507
|
+
Defining methods in your loaded files enables you to effectively define a
|
508
|
+
[DSL](https://en.wikipedia.org/wiki/Domain-specific_language) for your command line use.
|
509
|
+
You could use different load files for different projects, domains, or contexts,
|
510
|
+
and define aliases or one line scripts to give them meaningful names.
|
511
|
+
For example, if I wrote code to work with Ansible and put it in `~/projects/ansible-tools/rexe-ansible.rb`,
|
512
|
+
I could define an alias in my startup script:
|
476
513
|
|
477
514
|
```
|
478
|
-
➜ ~ alias rxans="rexe -l ~/projects/rexe-ansible.rb $*"
|
515
|
+
➜ ~ alias rxans="rexe -l ~/projects/ansible-tools/rexe-ansible.rb $*"
|
479
516
|
```
|
480
517
|
...and then I would have an Ansible DSL available for me to use by calling `rxans`.
|
481
518
|
|
@@ -505,32 +542,81 @@ true
|
|
505
542
|
This could be really handy if you call `pry` on a custom object that has methods especially suited to your task:
|
506
543
|
|
507
544
|
```
|
508
|
-
➜ ~ rexe -r
|
545
|
+
➜ ~ rexe -r wifi-wand,pry WifiWand::MacOsModel.new.pry
|
546
|
+
[1] pry(#<WifiWand::MacOsModel>)> random_mac_address
|
547
|
+
"a1:ea:69:d9:ca:05"
|
548
|
+
[2] pry(#<WifiWand::MacOsModel>)> connected_network_name
|
549
|
+
"My WiFi"
|
509
550
|
```
|
510
551
|
|
511
552
|
Ruby is supremely well suited for DSL's since it does not require parentheses for method calls,
|
512
553
|
so calls to your custom methods _look_ like built in language commands and keywords.
|
513
554
|
|
514
555
|
|
515
|
-
### Quoting Strings in Your Ruby Code
|
556
|
+
### Quotation Marks and Quoting Strings in Your Ruby Code
|
516
557
|
|
517
|
-
One complication of using utilities like `rexe` where Ruby code is specified on the
|
558
|
+
One complication of using utilities like `rexe` where Ruby code is specified on the command line is that
|
518
559
|
you need to be careful about the shell's special treatment of certain characters. For this reason, it is often
|
519
560
|
necessary to quote the Ruby code. You can use single or double quotes to have the shell treat your source code
|
520
|
-
as a single argument.
|
521
|
-
|
561
|
+
as a single argument. An excellent reference for how they differ is on StackOverflow at
|
562
|
+
https://stackoverflow.com/questions/6697753/difference-between-single-and-double-quotes-in-bash.
|
563
|
+
|
564
|
+
Personally, I find single quotes more useful since I usually don't want special characters in my Ruby code
|
565
|
+
like `$` to be processed by the shell.
|
566
|
+
|
567
|
+
Sometimes it doesn't matter:
|
568
|
+
|
569
|
+
```
|
570
|
+
➜ ~ rexe 'puts "hello"'
|
571
|
+
hello
|
572
|
+
➜ ~ rexe "puts 'hello'"
|
573
|
+
hello
|
574
|
+
```
|
575
|
+
|
576
|
+
We can also use `%q` or `%Q`, and sometimes this eliminates the needs for the outer quotes:
|
577
|
+
|
578
|
+
```
|
579
|
+
➜ ~ rexe puts %q{hello}
|
580
|
+
hello
|
581
|
+
➜ ~ rexe puts %Q{hello}
|
582
|
+
hello
|
583
|
+
```
|
584
|
+
|
585
|
+
Sometimes the quotes to use on the outside (quoting your command in the shell) need to be chosen
|
586
|
+
based on which quotes are needed on the inside. For example, in the following command, we need
|
587
|
+
double quotes in Ruby in order for interpolation to work, so we use single quotes on the outside:
|
588
|
+
|
589
|
+
```
|
590
|
+
➜ ~ rexe puts '"The time is now #{Time.now}"'
|
591
|
+
The time is now 2019-03-29 16:41:26 +0800
|
592
|
+
```
|
593
|
+
|
594
|
+
In this case we also need to use single quotes on the outside,
|
595
|
+
because we need literal double quotes in a `%Q{}` expression:
|
596
|
+
|
597
|
+
```
|
598
|
+
➜ ~ rexe 'puts %Q{The operating system name is "#{`uname`.chomp}".}'
|
599
|
+
The operating system name is "Darwin".
|
600
|
+
```
|
601
|
+
|
602
|
+
We can eliminate the need for any quotes in the Ruby code using `%Q{}`:
|
603
|
+
|
604
|
+
```
|
605
|
+
➜ ~ rexe puts '%Q{The time is now #{Time.now}}'
|
606
|
+
The time is now 2019-03-29 17:06:13 +0800
|
607
|
+
```
|
608
|
+
|
609
|
+
Of course you can always escape the quotes with backslashes instead, but in my experience
|
610
|
+
that becomes difficult to read.
|
522
611
|
|
523
|
-
Personally, I find single quotes more useful since I usually don't want special characters in my Ruby code like `$` to be processed by the shell.
|
524
612
|
|
525
|
-
When specifying the Ruby code, feel free to fall back on Ruby's super useful `%q{}` and `%Q{}`,
|
526
|
-
equivalent to single and double quotes, respectively.
|
527
613
|
|
528
614
|
|
529
615
|
### No Op Mode
|
530
616
|
|
531
617
|
The `-n` no-op mode will result in the specified source code _not_ being executed. This can sometimes be handy
|
532
|
-
in conjunction with a `-g` (logging) option
|
533
|
-
|
618
|
+
in conjunction with a `-g` (logging) option, if you have are building a rexe command and
|
619
|
+
want to inspect the configuration options before executing the Ruby code.
|
534
620
|
|
535
621
|
|
536
622
|
### Mimicking Method Arguments
|
@@ -542,15 +628,16 @@ One of the previous examples downloaded currency conversion rates.
|
|
542
628
|
To prepare for an example of how to do this, let's find out the available currency codes:
|
543
629
|
|
544
630
|
```
|
545
|
-
➜ / echo $EUR_RATES_JSON |
|
631
|
+
➜ / echo $EUR_RATES_JSON | \
|
632
|
+
rexe -ij -mb -op "self['rates'].keys.sort.join(' ')"
|
546
633
|
AUD BGN BRL CAD CHF CNY CZK DKK GBP HKD HRK HUF IDR ILS INR ISK JPY KRW MXN MYR NOK NZD PHP PLN RON RUB SEK SGD THB TRY USD ZAR
|
547
634
|
```
|
548
635
|
|
549
|
-
The codes output are the legal arguments that could be sent to `rexe`'s stdin as an argument.
|
636
|
+
The codes output are the legal arguments that could be sent to `rexe`'s stdin as an argument in the command below.
|
550
637
|
Let's find out the Euro exchange rate for _PHP_, Philippine Pesos:
|
551
638
|
|
552
639
|
```
|
553
|
-
➜ ~ echo PHP | rexe -ml -rjson \
|
640
|
+
➜ ~ echo PHP | rexe -ml -op -rjson \
|
554
641
|
"rate = JSON.parse(ENV['EUR_RATES_JSON'])['rates'][self];\
|
555
642
|
%Q{1 EUR = #{rate} #{self}}"
|
556
643
|
|
@@ -570,21 +657,32 @@ make the command line too complex to be practical.
|
|
570
657
|
|
571
658
|
Sometimes when editing text I need to do some one off text manipulation.
|
572
659
|
Using the system's commands for pasting to and copying from the clipboard,
|
573
|
-
this can easily be done. On the Mac,
|
574
|
-
|
575
|
-
|
576
|
-
|
660
|
+
this can easily be done. On the Mac, we have the following commands:
|
661
|
+
|
662
|
+
* `pbcopy` - copies the content of stdin _to_ the clipboard
|
663
|
+
* `pbpaste` - copies the content _from_ the clipboard to stdout
|
664
|
+
|
665
|
+
Let's say I have the following currency codes displayed on the screen
|
666
|
+
(data abridged for brevity):
|
577
667
|
|
578
668
|
```
|
579
|
-
AUD BGN BRL
|
669
|
+
AUD BGN BRL PHP TRY USD ZAR
|
580
670
|
```
|
581
671
|
|
582
672
|
...and I want to turn them into Ruby symbols for inclusion in Ruby source code as keys in a hash
|
583
673
|
whose values will be the display names of the currencies, e.g "Australian Dollar").
|
674
|
+
|
675
|
+
I could manually select that text and use system menu commands or keys to copy it to the clipboard,
|
676
|
+
or I could do this:
|
677
|
+
|
678
|
+
```
|
679
|
+
➜ ~ echo AUD BGN BRL PHP TRY USD ZAR | pbcopy
|
680
|
+
```
|
681
|
+
|
584
682
|
After copying this line to the clipboard, I could run this:
|
585
683
|
|
586
684
|
```
|
587
|
-
➜ ~ pbpaste | rexe -ml "split.map(&:downcase).map { |s| %Q{ #{s}: '',} }.join(%Q{\n})"
|
685
|
+
➜ ~ pbpaste | rexe -ml -op "split.map(&:downcase).map { |s| %Q{ #{s}: '',} }.join(%Q{\n})"
|
588
686
|
aud: '',
|
589
687
|
bgn: '',
|
590
688
|
brl: '',
|
@@ -592,7 +690,7 @@ After copying this line to the clipboard, I could run this:
|
|
592
690
|
```
|
593
691
|
|
594
692
|
If I add `| pbcopy` to the rexe command, then that output text would be copied into the clipboard instead of
|
595
|
-
displayed in the terminal, and I could then paste it
|
693
|
+
displayed in the terminal, and I could then paste it into my editor.
|
596
694
|
|
597
695
|
Using the clipboard in manual operations is handy, but using it in automated scripts is a very bad idea, since
|
598
696
|
there is only one clipboard per user session. If you use the clipboard in an automated script you risk
|
@@ -604,7 +702,8 @@ when you change the content of the clipboard.
|
|
604
702
|
Although `rexe` is cleanest with short one liners, you may want to use it to include nontrivial Ruby code
|
605
703
|
in your shell script as well. If you do this, you may need to add trailing backslashes to the lines of Ruby code.
|
606
704
|
|
607
|
-
What might not be so obvious is that you will often need to use semicolons
|
705
|
+
What might not be so obvious is that you will often need to use semicolons as statement separators.
|
706
|
+
For example, here is an example without a semicolon:
|
608
707
|
|
609
708
|
```
|
610
709
|
➜ ~ cowsay hello | rexe -me "print %Q{\u001b[33m} \
|
@@ -617,7 +716,13 @@ puts to_a"
|
|
617
716
|
```
|
618
717
|
|
619
718
|
The shell combines all backslash terminated lines into a single line of text, so when the Ruby
|
620
|
-
interpreter sees your code, it's all in a single line
|
719
|
+
interpreter sees your code, it's all in a single line:
|
720
|
+
|
721
|
+
```
|
722
|
+
➜ ~ cowsay hello | rexe -me "print %Q{\u001b[33m} puts to_a"
|
723
|
+
```
|
724
|
+
|
725
|
+
Adding the semicolon fixes the problem:
|
621
726
|
|
622
727
|
```
|
623
728
|
➜ ~ cowsay hello | rexe -me "print %Q{\u001b[33m}; \
|
@@ -642,15 +747,24 @@ but you want to override it for a single invocation. Here are your options:
|
|
642
747
|
1) Unspecify _all_ the requires or loads with the `-r!` and `-l!` command line options, respectively.
|
643
748
|
|
644
749
|
2) Unspecify individual requires or loads by preceding the name with `-`, e.g. `-r -rails`.
|
645
|
-
Array subtraction is used,
|
750
|
+
Array subtraction is used, and array subtraction removes _all_
|
751
|
+
occurrences of each element of the subtracted (subtrahend) array, so:
|
646
752
|
|
647
753
|
```
|
648
|
-
➜ ~ rexe -n -r rails,rails,rails,-rails -
|
754
|
+
➜ ~ rexe -n -r rails,rails,rails,-rails -gP
|
755
|
+
...
|
756
|
+
:requires=>["pp"],
|
757
|
+
...
|
649
758
|
```
|
650
759
|
|
651
760
|
...would show that the final `-rails` cancelled all the previous `rails` specifications.
|
652
761
|
|
762
|
+
We could have also extracted the requires list programmatically using `$RC` (described above) by doing this:
|
653
763
|
|
764
|
+
```
|
765
|
+
➜ ~ rexe -oP -r rails,rails,rails,-rails '$RC[:options][:requires]'
|
766
|
+
["pp"]
|
767
|
+
```
|
654
768
|
|
655
769
|
|
656
770
|
### Clearing _All_ Options
|
@@ -676,7 +790,7 @@ Files loaded with the `-l` option are treated the same way.
|
|
676
790
|
|
677
791
|
### Beware of Configured Requires
|
678
792
|
|
679
|
-
Requiring gems and modules for _all_ invocations of `rexe` will make your commands simpler and more concise, but will be a waste of execution time if they are not needed. You can inspect the execution times to see just how much time is being wasted. For example, we can find out that rails takes about 0.63 seconds to load on my laptop by observing and comparing the execution times with and without the require (output has been abbreviated using
|
793
|
+
Requiring gems and modules for _all_ invocations of `rexe` will make your commands simpler and more concise, but will be a waste of execution time if they are not needed. You can inspect the execution times to see just how much time is being wasted. For example, we can find out that rails takes about 0.63 seconds to load on my laptop by observing and comparing the execution times with and without the require (output has been abbreviated using `grep`):
|
680
794
|
|
681
795
|
```
|
682
796
|
➜ ~ rexe -gy -r rails 123 2>&1 | grep duration
|
@@ -700,40 +814,18 @@ Here are some more examples to illustrate the use of `rexe`.
|
|
700
814
|
|
701
815
|
----
|
702
816
|
|
703
|
-
### Format Conversions -- JSON, YAML, AwesomePrint
|
704
|
-
|
705
|
-
You may work with YAML or JSON a lot and need to inspect files from time to time.
|
706
|
-
`rexe` makes it easy to convert from one format to another. For example, here's a
|
707
|
-
command to convert from `countries.yaml` to AwesomePrint:
|
708
|
-
|
709
|
-
```
|
710
|
-
cat countries.yaml | rexe -iy -oa -mb self
|
711
|
-
```
|
712
|
-
|
713
|
-
You can easily create a script that automates this. Using your favorite editor,
|
714
|
-
put this in a file:
|
715
|
-
|
716
|
-
```
|
717
|
-
cat $1 | rexe -iy -oa -mb self
|
718
|
-
```
|
719
|
-
|
720
|
-
I put mine in a file called `y2a` in my `~/bin` directory where I keep custom scripts like this.
|
721
|
-
|
722
|
-
Now you can call `y2a` to output any YAML file using AwesomePrint.
|
723
|
-
|
724
|
-
----
|
725
817
|
|
726
818
|
#### Outputting ENV
|
727
819
|
|
728
|
-
Output the contents of `ENV` using AwesomePrint [^3]:
|
820
|
+
Output the contents of `ENV` using AwesomePrint [see footnote ^3]:
|
729
821
|
|
730
822
|
```
|
731
823
|
➜ ~ rexe -oa ENV
|
732
824
|
{
|
733
825
|
...
|
734
|
-
|
735
|
-
|
736
|
-
|
826
|
+
"LANG" => "en_US.UTF-8",
|
827
|
+
"PWD" => "/Users/kbennett/work/rexe",
|
828
|
+
"SHELL" => "/bin/zsh",
|
737
829
|
...
|
738
830
|
}
|
739
831
|
```
|
@@ -759,7 +851,7 @@ Show disk space used/free on a Mac's main hard drive's main partition:
|
|
759
851
|
Show the 3 longest file names of the current directory, with their lengths, in descending order:
|
760
852
|
|
761
853
|
```
|
762
|
-
➜ ~ ls | rexe -ml "%Q{[%4d] %s} % [length, self]" | sort -r | head -3
|
854
|
+
➜ ~ ls | rexe -ml -op "%Q{[%4d] %s} % [length, self]" | sort -r | head -3
|
763
855
|
[ 50] Agoda_Booking_ID_9999999 49_–_RECEIPT_enclosed.pdf
|
764
856
|
[ 40] 679a5c034994544aab4635ecbd50ab73-big.jpg
|
765
857
|
[ 28] 2018-abc-2019-01-16-2340.zip
|
@@ -789,13 +881,11 @@ alphabetically will result in sorting them numerically as well.
|
|
789
881
|
```
|
790
882
|
|
791
883
|
|
792
|
-
----
|
793
|
-
|
794
884
|
----
|
795
885
|
|
796
886
|
#### More YouTube: Differentiating Success and Failure
|
797
887
|
|
798
|
-
Let's
|
888
|
+
Let's take the YouTube example from the "Loading Files" section further.
|
799
889
|
Let's have the video that loads be different for the success or failure
|
800
890
|
of the command.
|
801
891
|
|
@@ -818,6 +908,8 @@ def play_result(success)
|
|
818
908
|
end
|
819
909
|
|
820
910
|
|
911
|
+
# Must pipe the exit code into this Ruby process,
|
912
|
+
# e.g. using `echo $? | rexe play_result_by_exit_code
|
821
913
|
def play_result_by_exit_code
|
822
914
|
play_result(STDIN.read.chomp == '0')
|
823
915
|
end
|
@@ -844,30 +936,33 @@ Then when I issue a command that succeeds, the Hallelujah Chorus is played:
|
|
844
936
|
Another formatting example...I wanted to reformat this source code...
|
845
937
|
|
846
938
|
```
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
939
|
+
'i' => Inspect
|
940
|
+
'j' => JSON
|
941
|
+
'J' => Pretty JSON
|
942
|
+
'n' => No Output
|
943
|
+
'p' => Puts (default)
|
944
|
+
's' => to_s
|
945
|
+
'y' => YAML
|
854
946
|
```
|
855
947
|
|
856
948
|
...into something more suitable for my help text.
|
857
949
|
Admittedly, the time it took to do this with rexe probably exceeded the time to do it manually,
|
858
|
-
but it was an interesting exercise and made it easy to try different formats. Here it is
|
950
|
+
but it was an interesting exercise and made it easy to try different formats. Here it is, after
|
951
|
+
copying the original text to the clipboard:
|
859
952
|
|
860
953
|
```
|
861
|
-
➜ ~ pbpaste | rexe -ml "sub(%q{'}, '-o').sub(%q{' =>}, %q{ })"
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
```
|
870
|
-
|
954
|
+
➜ ~ pbpaste | rexe -ml -op "sub(%q{'}, '-o').sub(%q{' =>}, %q{ })"
|
955
|
+
-oi Inspect
|
956
|
+
-oj JSON
|
957
|
+
-oJ Pretty JSON
|
958
|
+
-on No Output
|
959
|
+
-op Puts (default)
|
960
|
+
-os to_s
|
961
|
+
-oy YAML
|
962
|
+
```
|
963
|
+
|
964
|
+
|
965
|
+
----
|
871
966
|
|
872
967
|
#### Reformatting Grep Output
|
873
968
|
|
@@ -887,7 +982,7 @@ lib/rock_books/documents/journal_entry.rb:
|
|
887
982
|
So this is what worked well for me:
|
888
983
|
|
889
984
|
```
|
890
|
-
➜ ~ grep Struct **/*.rb | grep -v OpenStruct | rexe -ml \
|
985
|
+
➜ ~ grep Struct **/*.rb | grep -v OpenStruct | rexe -ml -op \
|
891
986
|
"a = \
|
892
987
|
gsub('lib/rock_books/', '')\
|
893
988
|
.gsub('< Struct.new', '')\
|
@@ -913,7 +1008,8 @@ types/acct_amount.rb class AcctAmount (:date, :code, :amount
|
|
913
1008
|
types/journal_entry_context.rb class JournalEntryContext (:journal, :linenum, :line)
|
914
1009
|
```
|
915
1010
|
|
916
|
-
Although there's a lot going on
|
1011
|
+
Although there's a lot going on in this code,
|
1012
|
+
the vertical and horizontal alignments and spacing make the code
|
917
1013
|
straightforward to follow. Here's what it does:
|
918
1014
|
|
919
1015
|
* grep the code base for `"Struct"`
|
@@ -923,6 +1019,8 @@ straightforward to follow. Here's what it does:
|
|
923
1019
|
* strip unwanted space because that will mess up the horizontal alignment of the output.
|
924
1020
|
* use C-style printf formatting to align the text into two columns
|
925
1021
|
|
1022
|
+
----
|
1023
|
+
|
926
1024
|
|
927
1025
|
### Conclusion
|
928
1026
|
|
@@ -933,8 +1031,8 @@ task at hand.
|
|
933
1031
|
|
934
1032
|
When we consider a new piece of software, we usually think "what would this be
|
935
1033
|
helpful with now?". However, for me, the power of `rexe` is not so much what I can do
|
936
|
-
with it in a single use case now, but rather what will I be able to do
|
937
|
-
as I accumulate more experience and expertise.
|
1034
|
+
with it in a single use case now, but rather what will I be able to do over time
|
1035
|
+
as I accumulate more experience and expertise with it.
|
938
1036
|
|
939
1037
|
I suggest starting to use `rexe` even for modest improvements in workflow, even
|
940
1038
|
if it doesn't seem compelling. There's a good chance that as you use it over
|
@@ -942,7 +1040,7 @@ time, new ideas will come to you and the workflow improvements will increase
|
|
942
1040
|
exponentially.
|
943
1041
|
|
944
1042
|
A word of caution though --
|
945
|
-
the complexity and difficulty of
|
1043
|
+
the complexity and difficulty of _sharing_ your `rexe` scripts across systems
|
946
1044
|
will be proportional to the extent to which you use environment variables
|
947
1045
|
and loaded files for configuration and shared code.
|
948
1046
|
Be responsible and disciplined in making this configuration and code as clean and organized as possible.
|
@@ -976,4 +1074,7 @@ Here is a _start_ at a method that opens a resource portably across operating sy
|
|
976
1074
|
end
|
977
1075
|
```
|
978
1076
|
|
979
|
-
[^3]: It is an interesting quirk of the Ruby language that `ENV.to_s` returns `"ENV"` and not the contents of the `ENV` object. As a result,
|
1077
|
+
[^3]: It is an interesting quirk of the Ruby language that `ENV.to_s` returns `"ENV"` and not the contents of the `ENV` object. As a result, many of the other output formats will also return some form of `"ENV"`. You can handle this by specifying `ENV.to_h`.
|
1078
|
+
|
1079
|
+
- Keith Bennett (@keithrbennett on Github and Twitter)
|
1080
|
+
|
data/exe/rexe
CHANGED
@@ -12,11 +12,20 @@ require 'shellwords'
|
|
12
12
|
|
13
13
|
class Rexe
|
14
14
|
|
15
|
-
VERSION = '0.
|
15
|
+
VERSION = '0.15.0'
|
16
16
|
|
17
17
|
PROJECT_URL = 'https://github.com/keithrbennett/rexe'
|
18
18
|
|
19
19
|
|
20
|
+
module Helpers
|
21
|
+
def error_exit(message)
|
22
|
+
$stderr.puts(message)
|
23
|
+
$stderr.puts("Use the -h option to get help.")
|
24
|
+
exit(-1)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
20
29
|
class Options < Struct.new(
|
21
30
|
:input_filespec,
|
22
31
|
:input_format,
|
@@ -127,6 +136,8 @@ class Rexe
|
|
127
136
|
|
128
137
|
class CommandLineParser
|
129
138
|
|
139
|
+
include Helpers
|
140
|
+
|
130
141
|
attr_reader :lookups, :options
|
131
142
|
|
132
143
|
def initialize
|
@@ -195,6 +206,9 @@ class Rexe
|
|
195
206
|
-r, --require REQUIRE(S) Gems and built-in libraries to require, comma separated;
|
196
207
|
! to clear all, or precede a name with '-' to remove
|
197
208
|
|
209
|
+
If source code is not specified, it will default to 'self', which is most likely useful
|
210
|
+
only in a filter mode (-ml, -me, -mb).
|
211
|
+
|
198
212
|
If there is an .rexerc file in your home directory, it will be run as Ruby code
|
199
213
|
before processing the input.
|
200
214
|
|
@@ -218,7 +232,21 @@ class Rexe
|
|
218
232
|
end
|
219
233
|
|
220
234
|
|
221
|
-
|
235
|
+
def open_resource(resource_identifier)
|
236
|
+
command = case (`uname`.chomp)
|
237
|
+
when 'Darwin'
|
238
|
+
'open'
|
239
|
+
when 'Linux'
|
240
|
+
'xdg-open'
|
241
|
+
else
|
242
|
+
'start'
|
243
|
+
end
|
244
|
+
|
245
|
+
`#{command} #{resource_identifier}`
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
# Using 'optparse', parses the command line.
|
222
250
|
# Settings go into this instance's properties (see Struct declaration).
|
223
251
|
def parse
|
224
252
|
|
@@ -230,7 +258,7 @@ class Rexe
|
|
230
258
|
parser.on('-f', '--input_file FILESPEC',
|
231
259
|
'Use this file instead of stdin; autodetects YAML and JSON file extensions') do |v|
|
232
260
|
unless File.exist?(v)
|
233
|
-
|
261
|
+
error_exit "File #{v} does not exist."
|
234
262
|
end
|
235
263
|
options.input_filespec = v
|
236
264
|
options.input_format = autodetect_file_format(v)
|
@@ -242,8 +270,7 @@ class Rexe
|
|
242
270
|
parser.on('-g', '--log_format FORMAT', 'Log format, logs to stderr, defaults to none (see -o for format options)') do |v|
|
243
271
|
options.log_format = lookups.output_formats[v]
|
244
272
|
if options.log_format.nil?
|
245
|
-
|
246
|
-
raise "Output mode was '#{v}' but must be one of #{lookups.output_formats.keys}."
|
273
|
+
error_exit("Output mode was '#{v}' but must be one of #{lookups.output_formats.keys}.")
|
247
274
|
end
|
248
275
|
end
|
249
276
|
|
@@ -257,8 +284,7 @@ class Rexe
|
|
257
284
|
|
258
285
|
options.input_format = lookups.input_formats[v]
|
259
286
|
if options.input_format.nil?
|
260
|
-
|
261
|
-
raise "Input mode was '#{v}' but must be one of #{lookups.input_formats.keys}."
|
287
|
+
error_exit("Input mode was '#{v}' but must be one of #{lookups.input_formats.keys}.")
|
262
288
|
end
|
263
289
|
end
|
264
290
|
|
@@ -271,7 +297,7 @@ class Rexe
|
|
271
297
|
|
272
298
|
existent, nonexistent = adds.partition { |filespec| File.exists?(filespec) }
|
273
299
|
if nonexistent.any?
|
274
|
-
|
300
|
+
error_exit("\nDid not find the following files to load: #{nonexistent}\n\n")
|
275
301
|
else
|
276
302
|
existent.each { |filespec| options.loads << filespec }
|
277
303
|
end
|
@@ -285,8 +311,7 @@ class Rexe
|
|
285
311
|
|
286
312
|
options.input_mode = lookups.input_modes[v]
|
287
313
|
if options.input_mode.nil?
|
288
|
-
|
289
|
-
raise "Input mode was '#{v}' but must be one of #{lookups.input_modes.keys}."
|
314
|
+
error_exit("Input mode was '#{v}' but must be one of #{lookups.input_modes.keys}.")
|
290
315
|
end
|
291
316
|
end
|
292
317
|
|
@@ -295,8 +320,7 @@ class Rexe
|
|
295
320
|
|
296
321
|
options.output_format = lookups.output_formats[v]
|
297
322
|
if options.output_format.nil?
|
298
|
-
|
299
|
-
raise "Output mode was '#{v}' but must be one of #{lookups.output_formats.keys}."
|
323
|
+
error_exit("Output mode was '#{v}' but must be one of #{lookups.output_formats.keys}.")
|
300
324
|
end
|
301
325
|
end
|
302
326
|
|
@@ -355,6 +379,8 @@ class Rexe
|
|
355
379
|
|
356
380
|
class Main
|
357
381
|
|
382
|
+
include Helpers
|
383
|
+
|
358
384
|
attr_reader :callable, :input_parser, :lookups,
|
359
385
|
:options, :output_formatter,
|
360
386
|
:log_formatter, :start_time, :user_source_code
|
@@ -366,20 +392,6 @@ class Rexe
|
|
366
392
|
end
|
367
393
|
|
368
394
|
|
369
|
-
def open_resource(resource_identifier)
|
370
|
-
command = case (`uname`.chomp)
|
371
|
-
when 'Darwin'
|
372
|
-
'open'
|
373
|
-
when 'Linux'
|
374
|
-
'xdg-open'
|
375
|
-
else
|
376
|
-
'start'
|
377
|
-
end
|
378
|
-
|
379
|
-
`#{command} #{resource_identifier}`
|
380
|
-
end
|
381
|
-
|
382
|
-
|
383
395
|
private def load_global_config_if_exists
|
384
396
|
filespec = File.join(Dir.home, '.rexerc')
|
385
397
|
load(filespec) if File.exists?(filespec)
|
@@ -428,10 +440,6 @@ class Rexe
|
|
428
440
|
|
429
441
|
|
430
442
|
private def create_callable
|
431
|
-
if user_source_code.empty? && (! options.noop)
|
432
|
-
raise "No source code provided. Use -h to display help."
|
433
|
-
end
|
434
|
-
|
435
443
|
eval("Proc.new { #{user_source_code} }")
|
436
444
|
end
|
437
445
|
|
@@ -483,6 +491,8 @@ class Rexe
|
|
483
491
|
options.loads.each { |file| load(file) }
|
484
492
|
|
485
493
|
@user_source_code = ARGV.join(' ')
|
494
|
+
@user_source_code = 'self' if @user_source_code == ''
|
495
|
+
|
486
496
|
@callable = create_callable
|
487
497
|
|
488
498
|
init_rexe_context
|
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.
|
4
|
+
version: 0.15.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-03-
|
11
|
+
date: 2019-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: awesome_print
|