rexe 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +232 -363
- data/exe/rexe +18 -14
- 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: 3da97ba4789e0253cb504694362282b40e5e06c153eda6c1b3c74e0a1836e5b7
|
4
|
+
data.tar.gz: a3d8ee332bd23f6b77c4a01ee1a0f011aab5131c7aca42ec79984d1e3b1ee55b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de3d6ffc907b7ef0fe448970671e6d6399a5f6eb86fecc88c805a88f1ae13ad7f71aad3339fddafce449711c9952319879894149651e6aeb5c021ba3b74a6d3a
|
7
|
+
data.tar.gz: 54e8c67c50a4e48dc612f170851462921ceab006d8788e8133a769c4abbd24a0adaace90453a594daaae5b3e3cb22b53577d19750fa37baefcbd6e7ba9ded71e
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -10,29 +10,24 @@ __Rexe__ is a Ruby script and gem that multiplies Ruby's usefulness and concisen
|
|
10
10
|
|
11
11
|
----
|
12
12
|
|
13
|
-
Shell scripting is great for simple tasks but for anything nontrivial
|
14
|
-
it can easily get cryptic and awkward (pun intended!).
|
13
|
+
Shell scripting is great for simple tasks but for anything nontrivial it can easily get cryptic and awkward (pun intended!).
|
15
14
|
|
16
|
-
This problem can often be solved by writing a Ruby script instead. Ruby provides fine
|
17
|
-
grained control in a language that is all about clarity, conciseness, and expressiveness.
|
15
|
+
This problem can often be solved by writing a Ruby script instead. Ruby provides fine grained control in a language that is all about clarity, conciseness, and expressiveness.
|
18
16
|
|
19
|
-
Unfortunately, when there are multiple OS commands to be called, then Ruby can be
|
20
|
-
awkward too.
|
17
|
+
Unfortunately, when there are multiple OS commands to be called, then Ruby can be awkward too.
|
21
18
|
|
22
|
-
Sometimes a good solution is to combine Ruby and shell scripting on the same
|
23
|
-
command line. Rexe multiplies your power to do so.
|
19
|
+
Sometimes a good solution is to combine Ruby and shell scripting on the same command line. Rexe multiplies your power to do so.
|
24
20
|
|
25
21
|
|
26
22
|
### Using the Ruby Interpreter on the Command Line
|
27
23
|
|
28
|
-
Let's start by seeing what the Ruby interpreter already provides.
|
29
|
-
Here we use `ruby` on the command line, using an intermediate environment variable to
|
30
|
-
simplify the logic and save the data for use by future commands.
|
31
|
-
An excerpt of the output follows the code:
|
24
|
+
Let's start by seeing what the Ruby interpreter already provides. Here we use `ruby` on the command line, using an intermediate environment variable to simplify the logic and save the data for use by future commands. An excerpt of the output follows the code:
|
32
25
|
|
33
|
-
```
|
26
|
+
```bash
|
34
27
|
➜ ~ export EUR_RATES_JSON=`curl https://api.exchangeratesapi.io/latest`
|
35
28
|
➜ ~ echo $EUR_RATES_JSON | ruby -r json -r yaml -e 'puts JSON.parse(STDIN.read).to_yaml'
|
29
|
+
```
|
30
|
+
```yaml
|
36
31
|
---
|
37
32
|
rates:
|
38
33
|
MXN: 21.96
|
@@ -43,23 +38,19 @@ base: EUR
|
|
43
38
|
date: '2019-03-08'
|
44
39
|
```
|
45
40
|
|
46
|
-
Unfortunately, the configuration setup (the `require`s) along with the reading, parsing, and formatting
|
47
|
-
make the command long and tedious, discouraging this approach.
|
41
|
+
Unfortunately, the configuration setup (the `require`s) along with the reading, parsing, and formatting make the command long and tedious, discouraging this approach.
|
48
42
|
|
49
43
|
### Rexe
|
50
44
|
|
51
|
-
Rexe [see footnote ^1 regarding its origin] can simplify such commands.
|
52
|
-
Among other things, rexe provides switch-activated input parsing and output formatting so that converting
|
53
|
-
from one format to another is trivial.
|
54
|
-
The previous `ruby` command can be expressed in `rexe` as:
|
45
|
+
Rexe [see footnote ^1 regarding its origin] can simplify such commands. Among other things, rexe provides switch-activated input parsing and output formatting so that converting from one format to another is trivial. The previous `ruby` command can be expressed in `rexe` as:
|
55
46
|
|
56
|
-
```
|
47
|
+
```bash
|
57
48
|
➜ ~ echo $EUR_RATES_JSON | rexe -mb -ij -oy self
|
58
49
|
```
|
59
50
|
|
60
51
|
Or, even more concisely (`self` is the default Ruby source code for rexe commands):
|
61
52
|
|
62
|
-
```
|
53
|
+
```bash
|
63
54
|
➜ ~ echo $EUR_RATES_JSON | rexe -mb -ij -oy
|
64
55
|
```
|
65
56
|
|
@@ -69,35 +60,35 @@ The command options may seem cryptic, but they're logical so it shouldn't take l
|
|
69
60
|
* `-ij` - parse that __input__ with __JSON__; `self` will be the parsed object
|
70
61
|
* `-oy` - __output__ the final value as __YAML__
|
71
62
|
|
72
|
-
If input comes from a JSON or YAML file, rexe determines the input format from the file's extension,
|
73
|
-
and it's even simpler:
|
63
|
+
If input comes from a JSON or YAML file, rexe determines the input format from the file's extension, and it's even simpler:
|
74
64
|
|
75
|
-
```
|
65
|
+
```bash
|
76
66
|
➜ ~ rexe -f eur_rates.json -oy
|
77
67
|
```
|
78
68
|
|
79
|
-
Rexe is at https://github.com/keithrbennett/rexe and can be installed with
|
80
|
-
`gem install rexe`. Rexe provides several ways to simplify Ruby on the command
|
81
|
-
line, tipping the scale so that it is practical to do it more often.
|
69
|
+
Rexe is at https://github.com/keithrbennett/rexe and can be installed with `gem install rexe`. Rexe provides several ways to simplify Ruby on the command line, tipping the scale so that it is practical to do it more often.
|
82
70
|
|
83
71
|
----
|
84
72
|
|
85
73
|
Here is rexe's help text as of the time of this writing:
|
86
74
|
|
87
75
|
```
|
88
|
-
rexe -- Ruby Command Line Executor/Filter -- v1.0.
|
76
|
+
rexe -- Ruby Command Line Executor/Filter -- v1.0.1 -- https://github.com/keithrbennett/rexe
|
89
77
|
|
90
|
-
Executes Ruby code on the command line,
|
91
|
-
|
78
|
+
Executes Ruby code on the command line,
|
79
|
+
optionally automating management of standard input and standard output,
|
80
|
+
and optionally parsing input and formatting output with YAML, JSON, etc.
|
92
81
|
|
93
82
|
rexe [options] [Ruby source code]
|
94
83
|
|
95
84
|
Options:
|
96
85
|
|
97
86
|
-c --clear_options Clear all previous command line options specified up to now
|
98
|
-
-f --input_file Use this file instead of stdin
|
99
|
-
|
100
|
-
|
87
|
+
-f --input_file Use this file instead of stdin for preprocessed input;
|
88
|
+
if filespec has a YAML and JSON file extension,
|
89
|
+
sets input format accordingly and sets input mode to -mb
|
90
|
+
-g --log_format FORMAT Log format, logs to stderr, defaults to none
|
91
|
+
(see -o for format options)
|
101
92
|
-h, --help Print help and exit
|
102
93
|
-i, --input_format FORMAT Input format
|
103
94
|
-ij JSON
|
@@ -106,13 +97,14 @@ Options:
|
|
106
97
|
-iy YAML
|
107
98
|
-l, --load RUBY_FILE(S) Ruby file(s) to load, comma separated;
|
108
99
|
! to clear all, or precede a name with '-' to remove
|
109
|
-
-m, --input_mode MODE
|
110
|
-
-ml line
|
111
|
-
-me enumerator
|
112
|
-
-mb big string
|
113
|
-
-mn (default) no input
|
114
|
-
|
115
|
-
|
100
|
+
-m, --input_mode MODE Input preprocessing mode (determines what `self` will be):
|
101
|
+
-ml line; each line is ingested as a separate string
|
102
|
+
-me enumerator (each_line on STDIN or File)
|
103
|
+
-mb big string; all lines combined into one string
|
104
|
+
-mn none (default); no input preprocessing;
|
105
|
+
self is an Object.new
|
106
|
+
-n, --[no-]noop Do not execute the code (useful with -g);
|
107
|
+
For true: yes, true, y, +; for false: no, false, n
|
116
108
|
-o, --output_format FORMAT Output format (defaults to puts):
|
117
109
|
-oi Inspect
|
118
110
|
-oj JSON
|
@@ -132,7 +124,7 @@ In many cases you will need to enclose your source code in single or double quot
|
|
132
124
|
If source code is not specified, it will default to 'self',
|
133
125
|
which is most likely useful only in a filter mode (-ml, -me, -mb).
|
134
126
|
|
135
|
-
If there is
|
127
|
+
If there is a .rexerc file in your home directory, it will be run as Ruby code
|
136
128
|
before processing the input.
|
137
129
|
|
138
130
|
If there is a REXE_OPTIONS environment variable, its content will be prepended
|
@@ -151,27 +143,23 @@ There are two main ways we can make the rexe command line even more concise:
|
|
151
143
|
|
152
144
|
### The REXE_OPTIONS Environment Variable
|
153
145
|
|
154
|
-
The `REXE_OPTIONS` environment variable can contain command line options that would otherwise
|
155
|
-
be specified on the rexe command line:
|
146
|
+
The `REXE_OPTIONS` environment variable can contain command line options that would otherwise be specified on the rexe command line:
|
156
147
|
|
157
148
|
Instead of this:
|
158
149
|
|
159
|
-
```
|
160
|
-
➜ ~ rexe -r wifi-wand -oa
|
150
|
+
```bash
|
151
|
+
➜ ~ rexe -r wifi-wand -oa WifiWand::MacOsModel.new.wifi_info
|
161
152
|
```
|
162
153
|
|
163
154
|
you can do this:
|
164
155
|
|
165
|
-
```
|
156
|
+
```bash
|
166
157
|
➜ ~ export REXE_OPTIONS="-r wifi-wand -oa"
|
167
158
|
➜ ~ rexe WifiWand::MacOsModel.new.wifi_info
|
168
159
|
➜ ~ # [more rexe commands with the same options]
|
169
160
|
```
|
170
161
|
|
171
|
-
Putting configuration options in `REXE_OPTIONS` effectively creates custom defaults,
|
172
|
-
and is useful when you use the same options in most or all of your commands.
|
173
|
-
Any options specified on the rexe
|
174
|
-
command line will override the environment variable options.
|
162
|
+
Putting configuration options in `REXE_OPTIONS` effectively creates custom defaults, and is useful when you use the same options in most or all of your commands. Any options specified on the rexe command line will override the environment variable options.
|
175
163
|
|
176
164
|
Like any environment variable, `REXE_OPTIONS` could also be set in your startup script, input on a command line using `export`, or in another script loaded with `source` or `.`.
|
177
165
|
|
@@ -181,42 +169,30 @@ The environment variable approach works well for command line _options_, but wha
|
|
181
169
|
|
182
170
|
For this, rexe lets you _load_ Ruby files, using the `-l` option, or implicitly (without your specifying it) in the case of the `~/.rexerc` file. Here is an example of something you might include in such a file:
|
183
171
|
|
184
|
-
```
|
172
|
+
```ruby
|
185
173
|
# Open YouTube to Wagner's "Ride of the Valkyries"
|
186
174
|
def valkyries
|
187
175
|
`open "http://www.youtube.com/watch?v=P73Z6291Pt8&t=0m28s"`
|
188
176
|
end
|
189
177
|
```
|
190
178
|
|
191
|
-
To digress a bit, why would you want this? You might want to be able to go to another room
|
192
|
-
until a long job completes, and be notified when it is done.
|
193
|
-
The `valkyries` method will launch a browser window pointed
|
194
|
-
to Richard Wagner's "Ride of the Valkyries"
|
195
|
-
starting at a lively point in the music [see footnote ^2 regarding autoplay].
|
196
|
-
(The `open` command is Mac specific and could be replaced with `start` on Windows,
|
197
|
-
a browser command name, etc.) [see footnote ^3 regarding OS portability].
|
179
|
+
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 [see footnote ^2 regarding autoplay]. (The `open` command is Mac specific and could be replaced with `start` on Windows, a browser command name, etc.) [see footnote ^3 regarding OS portability].
|
198
180
|
|
199
181
|
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.
|
200
182
|
|
201
|
-
Here is an example of how you might use the `valkyries` method, assuming the above configuration
|
202
|
-
is loaded from your `~/.rexerc` file or an explicitly loaded file:
|
183
|
+
Here is an example of how you might use the `valkyries` method, assuming the above configuration is loaded from your `~/.rexerc` file or an explicitly loaded file:
|
203
184
|
|
204
|
-
```
|
185
|
+
```bash
|
205
186
|
➜ ~ tar czf /tmp/my-whole-user-space.tar.gz ~ ; rexe valkyries
|
206
187
|
```
|
207
188
|
|
208
189
|
(Note that `;` is used rather than `&&` because we want to hear the music whether or not the command succeeds.)
|
209
190
|
|
210
|
-
You might be thinking that creating an alias or a minimal shell script (instead of a Ruby script)
|
211
|
-
for this `open` would be a simpler and more natural
|
212
|
-
approach, and I would agree with you. However, over time the number of these could become unmanageable, whereas using Ruby
|
213
|
-
you could build a pretty extensive and well organized library of functionality.
|
214
|
-
Moreover, that functionality could be made available to _all_ your Ruby code
|
215
|
-
(for example, by putting it in a gem), and not just command line one liners.
|
191
|
+
You might be thinking that creating an alias or a minimal shell script (instead of a Ruby script) for this `open` would be a simpler and more natural approach, and I would agree with you. However, over time the number of these could become unmanageable, whereas using Ruby you could build a pretty extensive and well organized library of functionality. Moreover, that functionality could be made available to _all_ your Ruby code (for example, by putting it in a gem), and not just command line one liners.
|
216
192
|
|
217
193
|
For example, you could have something like this in a gem or loaded file:
|
218
194
|
|
219
|
-
```
|
195
|
+
```ruby
|
220
196
|
def play(piece_code)
|
221
197
|
pieces = {
|
222
198
|
hallelujah: "https://www.youtube.com/watch?v=IUZEtVbJT5c&t=0m20s",
|
@@ -230,31 +206,27 @@ end
|
|
230
206
|
|
231
207
|
...which you could then call like this:
|
232
208
|
|
233
|
-
```
|
209
|
+
```bash
|
234
210
|
➜ ~ tar czf /tmp/my-whole-user-space.tar.gz ~ ; rexe 'play(:hallelujah)'
|
235
211
|
```
|
236
212
|
|
237
|
-
(You need to quote the `play` call because otherwise the shell will process and remove the parentheses.
|
238
|
-
Alternatively you could escape the parentheses with backslashes.)
|
213
|
+
(You need to quote the `play` call because otherwise the shell will process and remove the parentheses. Alternatively you could escape the parentheses with backslashes.)
|
239
214
|
|
240
215
|
One of the examples at the end of this articles shows how you could have different music play for success and failure.
|
241
216
|
|
242
217
|
|
243
218
|
### Logging
|
244
219
|
|
245
|
-
A log entry is optionally output to standard error after completion of the code.
|
246
|
-
This entry is a hash representation (to be precise, `to_h`) of the `$RC` OpenStruct described in the $RC section below.
|
247
|
-
It contains the version, date/time of execution, source code
|
248
|
-
to be evaluated, options (after parsing both the `REXE_OPTIONS` environment variable and the command line),
|
249
|
-
and the execution time of your Ruby code:
|
220
|
+
A log entry is optionally output to standard error after completion of the code. This entry is a hash representation (to be precise, `to_h`) of the `$RC` OpenStruct described in the $RC section below. It contains the version, date/time of execution, source code to be evaluated, options (after parsing both the `REXE_OPTIONS` environment variable and the command line), and the execution time of your Ruby code:
|
250
221
|
|
222
|
+
```bash
|
223
|
+
➜ ~ echo $EUR_RATES_JSON | rexe -gy -ij -mb -oa -n self
|
251
224
|
```
|
252
|
-
|
253
|
-
...
|
225
|
+
```yaml
|
254
226
|
---
|
255
|
-
:count:
|
227
|
+
:count: 0
|
256
228
|
:rexe_version: 1.0.0
|
257
|
-
:start_time: '2019-04-
|
229
|
+
:start_time: '2019-04-15T13:12:15+08:00'
|
258
230
|
:source_code: self
|
259
231
|
:options:
|
260
232
|
:input_filespec:
|
@@ -267,68 +239,54 @@ and the execution time of your Ruby code:
|
|
267
239
|
- json
|
268
240
|
- yaml
|
269
241
|
:log_format: :yaml
|
270
|
-
:noop:
|
271
|
-
:duration_secs: 0.
|
272
|
-
```
|
242
|
+
:noop: true
|
243
|
+
:duration_secs: 0.050326
|
244
|
+
```
|
273
245
|
|
274
|
-
We specified `-gy` for YAML format; there are other formats as well (see the help output or this document)
|
275
|
-
and the default is `-gn`, which means don't output the log entry at all.
|
246
|
+
We specified `-gy` for YAML format; there are other formats as well (see the help output or this document) and the default is `-gn`, which means don't output the log entry at all.
|
276
247
|
|
277
|
-
The requires you see were not explicitly specified but were automatically added because
|
278
|
-
Rexe will add any requires needed for automatic parsing and formatting, and
|
279
|
-
we specified those formats in the command line options `-gy -ij -oa`.
|
248
|
+
The requires you see were not explicitly specified but were automatically added because Rexe will add any requires needed for automatic parsing and formatting, and we specified those formats in the command line options `-gy -ij -oa`.
|
280
249
|
|
281
|
-
This extra output is sent to standard error (_stderr_) instead of standard output
|
282
|
-
(_stdout_) so that it will not pollute the "real" data when stdout is piped to
|
283
|
-
another command.
|
250
|
+
This extra output is sent to standard error (_stderr_) instead of standard output (_stdout_) so that it will not pollute the "real" data when stdout is piped to another command.
|
284
251
|
|
285
252
|
If you would like to append this informational output to a file(e.g. `rexe.log`), you could do something like this:
|
286
253
|
|
287
|
-
```
|
254
|
+
```bash
|
288
255
|
➜ ~ rexe ... -gy 2>>rexe.log
|
289
256
|
```
|
290
257
|
|
291
258
|
|
292
259
|
### Input Modes
|
293
260
|
|
294
|
-
Rexe tries to make it simple and convenient for you to handle standard input,
|
295
|
-
and in different ways. Here is the help text relating to input modes:
|
261
|
+
Rexe tries to make it simple and convenient for you to handle standard input, and in different ways. Here is the help text relating to input modes:
|
296
262
|
|
297
263
|
```
|
298
|
-
-m, --input_mode MODE
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
264
|
+
-m, --input_mode MODE Input preprocessing mode (determines what `self` will be):
|
265
|
+
-ml line; each line is ingested as a separate string
|
266
|
+
-me enumerator (each_line on STDIN or File)
|
267
|
+
-mb big string; all lines combined into one string
|
268
|
+
-mn none (default); no input preprocessing;
|
269
|
+
self is an Object.new
|
303
270
|
```
|
304
271
|
|
305
|
-
The first three are _filter_ modes; they make standard input available
|
306
|
-
to your code as `self`.
|
272
|
+
The first three are _filter_ modes; they make standard input available to your code as `self`.
|
307
273
|
|
308
|
-
The last (and default) is the _executor_ mode. It merely assists you in
|
309
|
-
executing the code you provide without any special implicit handling of standard input.
|
310
|
-
Here is more detail on these modes:
|
274
|
+
The last (and default) is the _executor_ mode. It merely assists you in executing the code you provide without any special implicit handling of standard input. Here is more detail on these modes:
|
311
275
|
|
312
276
|
|
313
277
|
#### -ml "Line" Filter Mode
|
314
278
|
|
315
|
-
In this mode, your code would be called once per line of input,
|
316
|
-
and in each call, `self` would evaluate to each line of text:
|
279
|
+
In this mode, your code would be called once per line of input, and in each call, `self` would evaluate to each line of text:
|
317
280
|
|
318
|
-
```
|
281
|
+
```bash
|
319
282
|
➜ ~ echo "hello\ngoodbye" | rexe -ml puts reverse
|
320
283
|
olleh
|
321
284
|
eybdoog
|
322
285
|
```
|
323
286
|
|
324
|
-
`reverse` is implicitly called on each line of standard input. `self`
|
325
|
-
is the input line in each call (we could also have used `self.reverse` but the `self.` would have been redundant).
|
287
|
+
`reverse` is implicitly called on each line of standard input. `self` is the input line in each call (we could also have used `self.reverse` but the `self.` would have been redundant).
|
326
288
|
|
327
|
-
Be aware that, in this mode, if you are using an automatic output mode
|
328
|
-
(anything other than the default `-on` no output mode),
|
329
|
-
although you can control the _content_ of output records,
|
330
|
-
there is no way to selectively _exclude_ records from being output. Even if the result of the code
|
331
|
-
is nil or the empty string, a newline will be output. To prevent this, you can do one of the following:
|
289
|
+
Be aware that, in this mode, if you are using an automatic output mode (anything other than the default `-on` no output mode), although you can control the _content_ of output records, there is no way to selectively _exclude_ records from being output. Even if the result of the code is nil or the empty string, a newline will be output. To prevent this, you can do one of the following:
|
332
290
|
|
333
291
|
* use `-me` Enumerator mode instead and call `select`, `filter`, `reject`, etc.
|
334
292
|
* use the (default) `-on` _no output_ mode and call `puts` explicitly for the output you _do_ want
|
@@ -336,16 +294,13 @@ is nil or the empty string, a newline will be output. To prevent this, you can d
|
|
336
294
|
|
337
295
|
#### -me "Enumerator" Filter Mode
|
338
296
|
|
339
|
-
In this mode, your code is called only once, and `self` is an enumerator
|
340
|
-
dispensing all lines of standard input. To be more precise, it is the enumerator returned by the `each_line` method,
|
341
|
-
on `$stdin` or the input file, whichever is applicable.
|
297
|
+
In this mode, your code is called only once, and `self` is an enumerator dispensing all lines of standard input. To be more precise, it is the enumerator returned by the `each_line` method, on `$stdin` or the input file, whichever is applicable.
|
342
298
|
|
343
299
|
Dealing with input as an enumerator enables you to use the wealth of `Enumerable` methods such as `select`, `to_a`, `map`, etc.
|
344
300
|
|
345
|
-
Here is an example of using `-me` to add line numbers to the first 3
|
346
|
-
files in the directory listing:
|
301
|
+
Here is an example of using `-me` to add line numbers to the first 3 files in the directory listing:
|
347
302
|
|
348
|
-
```
|
303
|
+
```bash
|
349
304
|
➜ ~ ls / | rexe -me "first(3).each_with_index { |ln,i| puts '%5d %s' % [i, ln] }"
|
350
305
|
|
351
306
|
0 AndroidStudioProjects
|
@@ -353,34 +308,26 @@ files in the directory listing:
|
|
353
308
|
2 Desktop
|
354
309
|
```
|
355
310
|
|
356
|
-
Since `self` is an enumerable, we can call `first` on it.
|
357
|
-
We've used the default output mode `-on` (_no output_ mode), which says don't do any automatic output,
|
358
|
-
just the output explicitly specified by `puts` in the source code.
|
311
|
+
Since `self` is an enumerable, we can call `first` on it. We've used the default output mode `-on` (_no output_ mode), which says don't do any automatic output, just the output explicitly specified by `puts` in the source code.
|
359
312
|
|
360
313
|
|
361
314
|
#### -mb "Big String" Filter Mode
|
362
315
|
|
363
|
-
In this mode, all standard input is combined into a single (possibly
|
364
|
-
large and possibly multiline) string.
|
316
|
+
In this mode, all standard input is combined into a single (possibly large and possibly multiline) string.
|
365
317
|
|
366
|
-
A good example of when you would use this is when you need to parse a multiline JSON or YAML representation of an object;
|
367
|
-
you need to pass all the standard input to the parse method.
|
368
|
-
This is the mode that was used in the first rexe example in this article.
|
318
|
+
A good example of when you would use this is when you need to parse a multiline JSON or YAML representation of an object; you need to pass all the standard input to the parse method. This is the mode that was used in the first rexe example in this article.
|
369
319
|
|
370
320
|
|
371
321
|
#### -mn "No Input" Executor Mode -- The Default
|
372
322
|
|
373
|
-
In this mode, no special handling of standard input is done at all;
|
374
|
-
if you want standard input you need to code it yourself (e.g. with `STDIN.read`).
|
323
|
+
In this mode, no special handling of standard input is done at all; if you want standard input you need to code it yourself (e.g. with `STDIN.read`).
|
375
324
|
|
376
|
-
`self` evaluates to a new instance of `Object`, which would be used
|
377
|
-
if you defined methods, constants, instance variables, etc., in your code.
|
325
|
+
`self` evaluates to a new instance of `Object`, which would be used if you defined methods, constants, instance variables, etc., in your code.
|
378
326
|
|
379
327
|
|
380
328
|
#### Filter Input Mode Memory Considerations
|
381
329
|
|
382
|
-
If you are using one of the filter modes, and may have more input than would fit in memory,
|
383
|
-
you can do one of the following:
|
330
|
+
If you are using one of the filter modes, and may have more input than would fit in memory, you can do one of the following:
|
384
331
|
|
385
332
|
* use `-ml` (line) mode so you are fed only 1 line at a time
|
386
333
|
* use an Enumerator, either by a) specifying the `-me` (enumerator) mode option,
|
@@ -392,20 +339,16 @@ you can do one of the following:
|
|
392
339
|
|
393
340
|
### Input Formats
|
394
341
|
|
395
|
-
Rexe can parse your input in any of several formats if you like.
|
396
|
-
You would request this in the _input format_ (`-i`) option.
|
397
|
-
Legal values are:
|
342
|
+
Rexe can parse your input in any of several formats if you like. You would request this in the _input format_ (`-i`) option. Legal values are:
|
398
343
|
|
399
344
|
* `-ij` - JSON
|
400
345
|
* `-im` - Marshal
|
401
346
|
* `-in` - [None] (default)
|
402
347
|
* `-iy` - YAML
|
403
348
|
|
404
|
-
Except for `-in`, which passes the text to your code untouched, your input will be parsed
|
405
|
-
in the specified format, and the resulting object passed into your code as `self`.
|
349
|
+
Except for `-in`, which passes the text to your code untouched, your input will be parsed in the specified format, and the resulting object passed into your code as `self`.
|
406
350
|
|
407
|
-
The input format option is ignored if the input _mode_ is `-mn` ("no input" executor mode, the default),
|
408
|
-
since there is no preprocessing of standard input in that mode.
|
351
|
+
The input format option is ignored if the input _mode_ is `-mn` ("no input" executor mode, the default), since there is no preprocessing of standard input in that mode.
|
409
352
|
|
410
353
|
### Output Formats
|
411
354
|
|
@@ -422,11 +365,9 @@ Several output formats are provided for your convenience:
|
|
422
365
|
|
423
366
|
All formats will implicitly `require` anything needed to accomplish their task (e.g. `require 'yaml'`).
|
424
367
|
|
425
|
-
The default is `-on` to produce no output at all (unless explicitly coded to do so). If you prefer a different
|
426
|
-
default such as `-op` for _puts_ mode, you can specify that in your `REXE_OPTIONS` environment variable.
|
368
|
+
The default is `-on` to produce no output at all (unless explicitly coded to do so). If you prefer a different default such as `-op` for _puts_ mode, you can specify that in your `REXE_OPTIONS` environment variable.
|
427
369
|
|
428
|
-
You may wonder why these formats are provided, given that their functionality
|
429
|
-
could be included in the custom code instead. Here's why:
|
370
|
+
You may wonder why these formats are provided, given that their functionality could be included in the custom code instead. Here's why:
|
430
371
|
|
431
372
|
* The savings in command line length goes a long way to making these commands more readable and feasible.
|
432
373
|
* It's much simpler to switch formats, as there is no need to change the code itself.
|
@@ -435,44 +376,40 @@ could be included in the custom code instead. Here's why:
|
|
435
376
|
|
436
377
|
### Reading Input from a File
|
437
378
|
|
438
|
-
Rexe also simplifies getting input from a file rather than standard input. The `-f` option takes a filespec
|
439
|
-
and does with its content exactly what it would have done with standard input. This shortens:
|
379
|
+
Rexe also simplifies getting input from a file rather than standard input. The `-f` option takes a filespec and does with its content exactly what it would have done with standard input. This shortens:
|
440
380
|
|
441
|
-
```
|
381
|
+
```bash
|
442
382
|
➜ ~ cat filename.ext | rexe ...
|
443
383
|
```
|
444
384
|
...to...
|
445
385
|
|
446
|
-
```
|
386
|
+
```bash
|
447
387
|
➜ ~ rexe -f filename.ext ...
|
448
388
|
```
|
449
389
|
|
450
|
-
This becomes even more useful if you are using files whose extensions are `.yml`, `.yaml`, or `.json` (case insensitively).
|
451
|
-
In this case the input format and mode will be set automatically for you to:
|
390
|
+
This becomes even more useful if you are using files whose extensions are `.yml`, `.yaml`, or `.json` (case insensitively). In this case the input format and mode will be set automatically for you to:
|
452
391
|
|
453
392
|
* `-iy` (YAML) or `-ij` (JSON) depending on the file extension
|
454
393
|
* `-mb` (one big string mode), which assumes that the most common use case will be to parse the entire file at once
|
455
394
|
|
456
395
|
So the example we gave above:
|
457
396
|
|
458
|
-
```
|
397
|
+
```bash
|
459
398
|
➜ ~ export EUR_RATES_JSON=`curl https://api.exchangeratesapi.io/latest`
|
460
399
|
➜ ~ echo $EUR_RATES_JSON | rexe -mb -ij -oy self
|
461
400
|
```
|
462
401
|
...could be changed to:
|
463
402
|
|
464
|
-
```
|
403
|
+
```bash
|
465
404
|
➜ ~ curl https://api.exchangeratesapi.io/latest > eur_rates.json
|
466
405
|
➜ ~ rexe -f eur_rates.json -oy self
|
467
406
|
```
|
468
407
|
|
469
|
-
Another possible win for using `-f` is that since it is a command line option, it could be specified in `REXE_OPTIONS`.
|
470
|
-
This could be useful if you are doing many operations on the same file.
|
408
|
+
Another possible win for using `-f` is that since it is a command line option, it could be specified in `REXE_OPTIONS`. This could be useful if you are doing many operations on the same file.
|
471
409
|
|
472
|
-
If you need to override the input mode and format automatically configured for file input, you can simply specify
|
473
|
-
the desired options on the command line _after_ the `-f`:
|
410
|
+
If you need to override the input mode and format automatically configured for file input, you can simply specify the desired options on the command line _after_ the `-f`:
|
474
411
|
|
475
|
-
```
|
412
|
+
```bash
|
476
413
|
➜ ~ rexe -f eur_rates.json -mb -in 'puts self.class, self[0..20]'
|
477
414
|
String
|
478
415
|
{"base":"EUR","rates"
|
@@ -481,15 +418,14 @@ String
|
|
481
418
|
|
482
419
|
### 'self' as Default Source Code
|
483
420
|
|
484
|
-
To make rexe even more concise, you do not need to specify any source code when you want that source code
|
485
|
-
to be `self`. This would be the case for simple format conversions, as in JSON to YAML conversion
|
486
|
-
mentioned above:
|
421
|
+
To make rexe even more concise, you do not need to specify any source code when you want that source code to be `self`. This would be the case for simple format conversions, as in JSON to YAML conversion mentioned above:
|
487
422
|
|
488
|
-
```
|
423
|
+
```bash
|
489
424
|
➜ ~ rexe -f eur_rates.json -oy
|
490
425
|
# or
|
491
426
|
➜ ~ echo $EUR_RATES_JSON | rexe -mb -ij -oy
|
492
|
-
|
427
|
+
```
|
428
|
+
```yaml
|
493
429
|
---
|
494
430
|
rates:
|
495
431
|
JPY: 126.63
|
@@ -498,44 +434,42 @@ rates:
|
|
498
434
|
...
|
499
435
|
```
|
500
436
|
|
501
|
-
This feature is probably only useful in the filter modes, since in the executor mode (`-mn`) self is a new
|
502
|
-
instance of `Object` and hardly ever useful as an output value.
|
437
|
+
This feature is probably only useful in the filter modes, since in the executor mode (`-mn`) self is a new instance of `Object` and hardly ever useful as an output value.
|
503
438
|
|
504
439
|
### The $RC Global OpenStruct
|
505
440
|
|
506
|
-
For your convenience, the information displayed in verbose mode is available to your code at runtime
|
507
|
-
by accessing the `$RC` global variable, which contains an OpenStruct. Let's print out its contents using AwesomePrint:
|
441
|
+
For your convenience, the information displayed in verbose mode is available to your code at runtime by accessing the `$RC` global variable, which contains an OpenStruct. Let's print out its contents using YAML:
|
508
442
|
|
509
|
-
```
|
510
|
-
➜ ~ rexe -
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
443
|
+
```bash
|
444
|
+
➜ ~ rexe -oy '$RC'
|
445
|
+
```
|
446
|
+
```yaml
|
447
|
+
--- !ruby/object:OpenStruct
|
448
|
+
table:
|
449
|
+
:count: 0
|
450
|
+
:rexe_version: 1.0.0
|
451
|
+
:start_time: '2019-04-15T13:25:56+08:00'
|
452
|
+
:source_code: "$RC"
|
453
|
+
:options:
|
454
|
+
:input_filespec:
|
455
|
+
:input_format: :none
|
456
|
+
:input_mode: :none
|
457
|
+
:loads: []
|
458
|
+
:output_format: :yaml
|
459
|
+
:requires:
|
460
|
+
- yaml
|
461
|
+
:log_format: :none
|
462
|
+
:noop: false
|
463
|
+
modifiable: true
|
529
464
|
```
|
530
465
|
|
531
|
-
Probably most useful in that object at runtime
|
532
|
-
is the record count, accessible with both `$RC.count` and `$RC.i`.
|
533
|
-
This is only really useful in line mode, because in the others
|
534
|
-
it will always be 0 or 1. Here is an example of how you might use it as a kind of progress indicator:
|
466
|
+
Probably most useful in that object at runtime is the record count, accessible with both `$RC.count` and `$RC.i`. This is only really useful in line mode, because in the others it will always be 0 or 1. Here is an example of how you might use it as a kind of progress indicator:
|
535
467
|
|
536
|
-
```
|
468
|
+
```bash
|
537
469
|
➜ ~ find / | rexe -ml -on \
|
538
470
|
'if $RC.i % 1000 == 0; puts %Q{File entry ##{$RC.i} is #{self}}; end'
|
471
|
+
```
|
472
|
+
```
|
539
473
|
...
|
540
474
|
File entry #106000 is /usr/local/Cellar/go/1.11.5/libexec/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go
|
541
475
|
File entry #107000 is /usr/local/Cellar/go/1.11.5/libexec/src/go/types/testdata/cycles1.src
|
@@ -543,30 +477,21 @@ File entry #108000 is /usr/local/Cellar/go/1.11.5/libexec/src/runtime/os_linux_n
|
|
543
477
|
...
|
544
478
|
```
|
545
479
|
|
546
|
-
Note that a single quote was used for the Ruby code here;
|
547
|
-
if a double quote were used, the `$RC` would have been interpreted
|
548
|
-
and removed by the shell.
|
480
|
+
Note that a single quote was used for the Ruby code here; if a double quote were used, the `$RC` would have been interpreted and removed by the shell.
|
549
481
|
|
550
482
|
|
551
483
|
### Implementing Domain Specific Languages (DSL's)
|
552
484
|
|
553
|
-
Defining methods in your loaded files enables you to effectively define a
|
554
|
-
[DSL](https://en.wikipedia.org/wiki/Domain-specific_language) for your command line use.
|
555
|
-
You could use different load files for different projects, domains, or contexts,
|
556
|
-
and define aliases or one line scripts to give them meaningful names.
|
557
|
-
For example, if you had Ansible helper code in `~/projects/ansible-tools/rexe-ansible.rb`,
|
558
|
-
you could define an alias in your startup script:
|
485
|
+
Defining methods in your loaded files enables you to effectively define a [DSL](https://en.wikipedia.org/wiki/Domain-specific_language) for your command line use. You could use different load files for different projects, domains, or contexts, and define aliases or one line scripts to give them meaningful names. For example, if you had Ansible helper code in `~/projects/ansible-tools/rexe-ansible.rb`, you could define an alias in your startup script:
|
559
486
|
|
560
|
-
```
|
487
|
+
```bash
|
561
488
|
➜ ~ alias rxans="rexe -l ~/projects/ansible-tools/rexe-ansible.rb $*"
|
562
489
|
```
|
563
490
|
...and then you would have an Ansible DSL available for me to use by calling `rxans`.
|
564
491
|
|
565
|
-
In addition, since you can also call `pry` on the context of any object, you
|
566
|
-
can provide a DSL in a [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) (shell)
|
567
|
-
trivially easily. Just to illustrate, here's how you would open a REPL on the File class:
|
492
|
+
In addition, since you can also call `pry` on the context of any object, you can provide a DSL in a [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) (shell) trivially easily. Just to illustrate, here's how you would open a REPL on the File class:
|
568
493
|
|
569
|
-
```
|
494
|
+
```bash
|
570
495
|
➜ ~ ruby -r pry -e File.pry
|
571
496
|
# or
|
572
497
|
➜ ~ rexe -r pry File.pry
|
@@ -574,9 +499,10 @@ trivially easily. Just to illustrate, here's how you would open a REPL on the Fi
|
|
574
499
|
|
575
500
|
`self` would evaluate to the `File` class, so you could call class methods using only their names:
|
576
501
|
|
577
|
-
```
|
502
|
+
```bash
|
578
503
|
➜ ~ rexe -r pry File.pry
|
579
|
-
|
504
|
+
```
|
505
|
+
```
|
580
506
|
[6] pry(File)> size '/etc/passwd'
|
581
507
|
6804
|
582
508
|
[7] pry(File)> directory? '.'
|
@@ -587,32 +513,28 @@ true
|
|
587
513
|
|
588
514
|
This could be really handy if you call `pry` on a custom object that has methods especially suited to your task:
|
589
515
|
|
590
|
-
```
|
516
|
+
```bash
|
591
517
|
➜ ~ rexe -r wifi-wand,pry WifiWand::MacOsModel.new.pry
|
518
|
+
```
|
519
|
+
```
|
592
520
|
[1] pry(#<WifiWand::MacOsModel>)> random_mac_address
|
593
521
|
"a1:ea:69:d9:ca:05"
|
594
522
|
[2] pry(#<WifiWand::MacOsModel>)> connected_network_name
|
595
523
|
"My WiFi"
|
596
524
|
```
|
597
525
|
|
598
|
-
Ruby is supremely well suited for DSL's since it does not require parentheses for method calls,
|
599
|
-
so calls to your custom methods _look_ like built in language commands and keywords.
|
526
|
+
Ruby is supremely well suited for DSL's since it does not require parentheses for method calls, so calls to your custom methods _look_ like built in language commands and keywords.
|
600
527
|
|
601
528
|
|
602
529
|
### Quotation Marks and Quoting Strings in Your Ruby Code
|
603
530
|
|
604
|
-
One complication of using utilities like rexe where Ruby code is specified on the command line is that
|
605
|
-
you need to be careful about the shell's special treatment of certain characters. For this reason, it is often
|
606
|
-
necessary to quote the Ruby code. You can use single or double quotes to have the shell treat your source code
|
607
|
-
as a single argument. An excellent reference for how they differ is on StackOverflow at
|
608
|
-
https://stackoverflow.com/questions/6697753/difference-between-single-and-double-quotes-in-bash.
|
531
|
+
One complication of using utilities like rexe where Ruby code is specified on the command line is that you need to be careful about the shell's special treatment of certain characters. For this reason, it is often necessary to quote the Ruby code. You can use single or double quotes to have the shell treat your source code as a single argument. An excellent reference for how they differ is on StackOverflow at https://stackoverflow.com/questions/6697753/difference-between-single-and-double-quotes-in-bash.
|
609
532
|
|
610
|
-
Personally, I find single quotes more useful since I usually don't want special characters in my Ruby code
|
611
|
-
like `$` to be processed by the shell.
|
533
|
+
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.
|
612
534
|
|
613
535
|
Sometimes it doesn't matter:
|
614
536
|
|
615
|
-
```
|
537
|
+
```bash
|
616
538
|
➜ ~ rexe 'puts "hello"'
|
617
539
|
hello
|
618
540
|
➜ ~ rexe "puts 'hello'"
|
@@ -621,67 +543,65 @@ hello
|
|
621
543
|
|
622
544
|
We can also use `%q` or `%Q`, and sometimes this eliminates the needs for the outer quotes altogether:
|
623
545
|
|
624
|
-
```
|
546
|
+
```bash
|
625
547
|
➜ ~ rexe puts %q{hello}
|
626
548
|
hello
|
627
549
|
➜ ~ rexe puts %Q{hello}
|
628
550
|
hello
|
629
551
|
```
|
630
552
|
|
631
|
-
Sometimes the quotes to use on the outside (quoting your command in the shell) need to be chosen
|
632
|
-
based on which quotes are needed on the inside. For example, in the following command, we need
|
633
|
-
double quotes in Ruby in order for interpolation to work, so we use single quotes on the outside:
|
553
|
+
Sometimes the quotes to use on the outside (quoting your command in the shell) need to be chosen based on which quotes are needed on the inside. For example, in the following command, we need double quotes in Ruby in order for interpolation to work, so we use single quotes on the outside:
|
634
554
|
|
635
|
-
```
|
555
|
+
```bash
|
636
556
|
➜ ~ rexe puts '"The time is now #{Time.now}"'
|
557
|
+
```
|
558
|
+
```
|
637
559
|
The time is now 2019-03-29 16:41:26 +0800
|
638
560
|
```
|
639
561
|
|
640
|
-
In this case we also need to use single quotes on the outside,
|
641
|
-
because we need literal double quotes in a `%Q{}` expression:
|
562
|
+
In this case we also need to use single quotes on the outside, because we need literal double quotes in a `%Q{}` expression:
|
642
563
|
|
643
|
-
```
|
564
|
+
```bash
|
644
565
|
➜ ~ rexe 'puts %Q{The operating system name is "#{`uname`.chomp}".}'
|
566
|
+
```
|
567
|
+
```
|
645
568
|
The operating system name is "Darwin".
|
646
569
|
```
|
647
570
|
|
648
571
|
We can eliminate the need for any quotes in the Ruby code using `%Q{}`:
|
649
572
|
|
650
|
-
```
|
573
|
+
```bash
|
651
574
|
➜ ~ rexe puts '%Q{The time is now #{Time.now}}'
|
575
|
+
```
|
576
|
+
```
|
652
577
|
The time is now 2019-03-29 17:06:13 +0800
|
653
578
|
```
|
654
579
|
|
655
580
|
Of course you can always escape the quotes with backslashes instead, but that is probably more difficult to read.
|
656
581
|
|
657
582
|
|
658
|
-
|
659
|
-
|
660
583
|
### No Op Mode
|
661
584
|
|
662
|
-
The `-n` no-op mode will result in the specified source code _not_ being executed. This can sometimes be handy
|
663
|
-
in conjunction with a `-g` (logging) option, if you have are building a rexe command and
|
664
|
-
want to inspect the configuration options before executing the Ruby code.
|
585
|
+
The `-n` no-op mode will result in the specified source code _not_ being executed. This can sometimes be handy in conjunction with a `-g` (logging) option, if you have are building a rexe command and want to inspect the configuration options before executing the Ruby code.
|
665
586
|
|
666
587
|
|
667
588
|
### Mimicking Method Arguments
|
668
589
|
|
669
|
-
You may want to support arguments in your rexe commands.
|
670
|
-
It's a little kludgy, but you could do this by piping in the arguments as rexe's stdin.
|
590
|
+
You may want to support arguments in your rexe commands. It's a little kludgy, but you could do this by piping in the arguments as rexe's stdin.
|
671
591
|
|
672
|
-
One of the previous examples downloaded currency conversion rates.
|
673
|
-
To prepare for an example of how to do this, let's find out the available currency codes:
|
592
|
+
One of the previous examples downloaded currency conversion rates. To prepare for an example of how to do this, let's find out the available currency codes:
|
674
593
|
|
675
|
-
```
|
594
|
+
```bash
|
676
595
|
➜ / echo $EUR_RATES_JSON | \
|
677
596
|
rexe -ij -mb -op "self['rates'].keys.sort.join(' ')"
|
597
|
+
```
|
598
|
+
```
|
678
599
|
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
|
679
600
|
```
|
680
601
|
|
681
|
-
The codes output are the legal arguments that could be sent to rexe's stdin as an argument in the command below.
|
682
|
-
Let's find out the Euro exchange rate for _PHP_, Philippine Pesos:
|
602
|
+
The codes output are the legal arguments that could be sent to rexe's stdin as an argument in the command below. Let's find out the Euro exchange rate for _PHP_, Philippine Pesos:
|
683
603
|
|
684
|
-
```
|
604
|
+
```bash
|
685
605
|
➜ ~ echo PHP | rexe -ml -op -rjson \
|
686
606
|
"rate = JSON.parse(ENV['EUR_RATES_JSON'])['rates'][self];\
|
687
607
|
%Q{1 EUR = #{rate} #{self}}"
|
@@ -689,88 +609,78 @@ Let's find out the Euro exchange rate for _PHP_, Philippine Pesos:
|
|
689
609
|
1 EUR = 58.986 PHP
|
690
610
|
```
|
691
611
|
|
692
|
-
In this code, `self` is the currency code `PHP` (Philippine Peso).
|
693
|
-
We have accessed the JSON text to parse from the environment variable we previously populated.
|
612
|
+
In this code, `self` is the currency code `PHP` (Philippine Peso). We have accessed the JSON text to parse from the environment variable we previously populated.
|
694
613
|
|
695
|
-
Because we "used up" stdin for the `PHP` argument, we needed to read the JSON data explicitly from
|
696
|
-
the environment variable, and that made the command more complex. A regular Ruby script would handle this more nicely.
|
614
|
+
Because we "used up" stdin for the `PHP` argument, we needed to read the JSON data explicitly from the environment variable, and that made the command more complex. A regular Ruby script would handle this more nicely.
|
697
615
|
|
698
616
|
|
699
617
|
### Using the Clipboard for Text Processing
|
700
618
|
|
701
|
-
For editing text in an editor,
|
702
|
-
rexe can be used for text transformations that would otherwise need to be done manually.
|
619
|
+
For editing text in an editor, rexe can be used for text transformations that would otherwise need to be done manually.
|
703
620
|
|
704
|
-
The system's commands for pasting to and copying from the clipboard can handle the moving of the text
|
705
|
-
between the editor and rexe. On the Mac, we have the following commands:
|
621
|
+
The system's commands for pasting to and copying from the clipboard can handle the moving of the text between the editor and rexe. On the Mac, we have the following commands:
|
706
622
|
|
707
623
|
* `pbcopy` - copies the content of its stdin _to_ the clipboard
|
708
624
|
* `pbpaste` - copies the content _from_ the clipboard to its stdout
|
709
625
|
|
710
|
-
Let's say we have the following currency codes displayed on the screen
|
711
|
-
(data abridged for brevity):
|
626
|
+
Let's say we have the following currency codes displayed on the screen (data abridged for brevity):
|
712
627
|
|
713
628
|
```
|
714
629
|
AUD BGN BRL PHP TRY USD ZAR
|
715
630
|
```
|
716
631
|
|
717
|
-
...and we want to turn them into Ruby symbols for inclusion in Ruby source code as keys in a hash
|
718
|
-
whose values will be the display names of the currencies, e.g "Australian Dollar").
|
632
|
+
...and we want to turn them into Ruby symbols for inclusion in Ruby source code as keys in a hash whose values will be the display names of the currencies, e.g "Australian Dollar").
|
719
633
|
|
720
|
-
We could manually select that text and use system menu commands or keys to copy it to the clipboard,
|
721
|
-
or we could do this:
|
634
|
+
We could manually select that text and use system menu commands or keys to copy it to the clipboard, or we could do this:
|
722
635
|
|
723
|
-
```
|
636
|
+
```bash
|
724
637
|
➜ ~ echo AUD BGN BRL PHP TRY USD ZAR | pbcopy
|
725
638
|
```
|
726
639
|
|
727
640
|
After copying this line to the clipboard, we could run this:
|
728
641
|
|
729
|
-
```
|
730
|
-
➜ ~ pbpaste | rexe -ml -op
|
642
|
+
```bash
|
643
|
+
➜ ~ pbpaste | rexe -ml -op \
|
644
|
+
"split.map(&:downcase).map { |s| %Q{ #{s}: '',} }.join(%Q{\n})"
|
731
645
|
aud: '',
|
732
646
|
bgn: '',
|
733
647
|
brl: '',
|
734
648
|
# ...
|
735
649
|
```
|
736
650
|
|
737
|
-
If I add `| pbcopy` to the rexe command, then that output text would be copied into the clipboard instead of
|
738
|
-
displayed in the terminal, and I could then paste it into my editor.
|
651
|
+
If I add `| pbcopy` to the rexe command, then that output text would be copied into the clipboard instead of displayed in the terminal, and I could then paste it into my editor.
|
739
652
|
|
740
|
-
Using the clipboard in manual operations is handy, but using it in automated scripts is a very bad idea, since
|
741
|
-
there is only one clipboard per user session. If you use the clipboard in an automated script you risk
|
742
|
-
an error situation if its content is changed by another process, or, conversely, you could mess up another process
|
743
|
-
when you change the content of the clipboard.
|
653
|
+
Using the clipboard in manual operations is handy, but using it in automated scripts is a very bad idea, since there is only one clipboard per user session. If you use the clipboard in an automated script you risk an error situation if its content is changed by another process, or, conversely, you could mess up another process when you change the content of the clipboard.
|
744
654
|
|
745
655
|
### Multiline Ruby Commands
|
746
656
|
|
747
|
-
Although rexe is cleanest with short one liners, you may want to use it to include nontrivial Ruby code
|
748
|
-
in your shell script as well. If you do this, you may need to add trailing backslashes to the lines of Ruby code.
|
657
|
+
Although rexe is cleanest with short one liners, you may want to use it to include nontrivial Ruby code in your shell script as well. If you do this, you may need to add trailing backslashes to the lines of Ruby code.
|
749
658
|
|
750
|
-
What might not be so obvious is that you will often need to use semicolons as statement separators.
|
751
|
-
For example, here is an example without a semicolon:
|
659
|
+
What might not be so obvious is that you will often need to use semicolons as statement separators. For example, here is an example without a semicolon:
|
752
660
|
|
753
|
-
```
|
661
|
+
```bash
|
754
662
|
➜ ~ cowsay hello | rexe -me "print %Q{\u001b[33m} \
|
755
663
|
puts to_a"
|
756
|
-
|
664
|
+
```
|
665
|
+
```
|
757
666
|
rexe: (eval):1: syntax error, unexpected tIDENTIFIER, expecting '}'
|
758
667
|
...new { print %Q{\u001b[33m} puts to_a }
|
759
668
|
... ^~~~
|
760
669
|
```
|
761
670
|
|
762
|
-
The shell combines all backslash terminated lines into a single line of text, so when the Ruby
|
763
|
-
interpreter sees your code, it's all in a single line:
|
671
|
+
The shell combines all backslash terminated lines into a single line of text, so when the Ruby interpreter sees your code, it's all in a single line:
|
764
672
|
|
765
|
-
```
|
673
|
+
```bash
|
766
674
|
➜ ~ cowsay hello | rexe -me "print %Q{\u001b[33m} puts to_a"
|
767
675
|
```
|
768
676
|
|
769
677
|
Adding the semicolon fixes the problem:
|
770
678
|
|
771
|
-
```
|
679
|
+
```bash
|
772
680
|
➜ ~ cowsay hello | rexe -me "print %Q{\u001b[33m}; \
|
773
681
|
puts to_a"
|
682
|
+
```
|
683
|
+
```
|
774
684
|
_______
|
775
685
|
< hello >
|
776
686
|
-------
|
@@ -784,17 +694,13 @@ puts to_a"
|
|
784
694
|
|
785
695
|
### Clearing the Require and Load Lists
|
786
696
|
|
787
|
-
There may be times when you have specified a load or require on the command line
|
788
|
-
or in the `REXE_OPTIONS` environment variable,
|
789
|
-
but you want to override it for a single invocation. Here are your options:
|
697
|
+
There may be times when you have specified a load or require on the command line or in the `REXE_OPTIONS` environment variable, but you want to override it for a single invocation. Here are your options:
|
790
698
|
|
791
699
|
1) Unspecify _all_ the requires or loads with the `-r!` and `-l!` command line options, respectively.
|
792
700
|
|
793
|
-
2) Unspecify individual requires or loads by preceding the name with `-`, e.g. `-r -rails`.
|
794
|
-
Array subtraction is used, and array subtraction removes _all_
|
795
|
-
occurrences of each element of the subtracted (subtrahend) array, so:
|
701
|
+
2) Unspecify individual requires or loads by preceding the name with `-`, e.g. `-r -rails`. Array subtraction is used, and array subtraction removes _all_ occurrences of each element of the subtracted (subtrahend) array, so:
|
796
702
|
|
797
|
-
```
|
703
|
+
```bash
|
798
704
|
➜ ~ rexe -n -r rails,rails,rails,-rails -gP
|
799
705
|
...
|
800
706
|
:requires=>["pp"],
|
@@ -805,7 +711,7 @@ occurrences of each element of the subtracted (subtrahend) array, so:
|
|
805
711
|
|
806
712
|
We could have also extracted the requires list programmatically using `$RC` (described above) by doing this:
|
807
713
|
|
808
|
-
```
|
714
|
+
```bash
|
809
715
|
➜ ~ rexe -oP -r rails,rails,rails,-rails '$RC[:options][:requires]'
|
810
716
|
["pp"]
|
811
717
|
```
|
@@ -813,17 +719,14 @@ We could have also extracted the requires list programmatically using `$RC` (des
|
|
813
719
|
|
814
720
|
### Clearing _All_ Options
|
815
721
|
|
816
|
-
You can also clear _all_ options specified up to a certain point in time with the _clear options_ option (`-c`).
|
817
|
-
This is especially useful if you have specified options in the `REXE_OPTIONS` environment variable,
|
818
|
-
and want to ignore all of them.
|
722
|
+
You can also clear _all_ options specified up to a certain point in time with the _clear options_ option (`-c`). This is especially useful if you have specified options in the `REXE_OPTIONS` environment variable, and want to ignore all of them.
|
819
723
|
|
820
724
|
|
821
725
|
### Comma Separated Requires and Loads
|
822
726
|
|
823
|
-
For consistency with the `ruby` interpreter, rexe supports requires with the `-r` option, but
|
824
|
-
also allows grouping them together using commas:
|
727
|
+
For consistency with the `ruby` interpreter, rexe supports requires with the `-r` option, but also allows grouping them together using commas:
|
825
728
|
|
826
|
-
```
|
729
|
+
```bash
|
827
730
|
vvvvvvvvvvvvvvvvvvvvv
|
828
731
|
➜ ~ echo $EUR_RATES_JSON | rexe -r json,awesome_print 'ap JSON.parse(STDIN.read)'
|
829
732
|
^^^^^^^^^^^^^^^^^^^^^
|
@@ -836,7 +739,7 @@ Files loaded with the `-l` option are treated the same way.
|
|
836
739
|
|
837
740
|
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 consumed. For example, we can find out that rails takes about 0.63 seconds to load on one system by observing and comparing the execution times with and without the require (output has been abbreviated using `grep`):
|
838
741
|
|
839
|
-
```
|
742
|
+
```bash
|
840
743
|
➜ ~ rexe -gy -r rails 2>&1 | grep duration
|
841
744
|
:duration_secs: 0.660138
|
842
745
|
➜ ~ rexe -gy 2>&1 | grep duration
|
@@ -847,9 +750,7 @@ Requiring gems and modules for _all_ invocations of rexe will make your commands
|
|
847
750
|
|
848
751
|
### Operating System Support
|
849
752
|
|
850
|
-
Rexe has been tested successfully on Mac OS, Linux, and Windows Subsystem for Linux (WSL).
|
851
|
-
It is intended as a tool for the Unix shell, and, as such, no attempt is made to support
|
852
|
-
Windows non-Unix shells.
|
753
|
+
Rexe has been tested successfully on Mac OS, Linux, and Windows Subsystem for Linux (WSL). It is intended as a tool for the Unix shell, and, as such, no attempt is made to support Windows non-Unix shells.
|
853
754
|
|
854
755
|
|
855
756
|
### More Examples
|
@@ -862,29 +763,28 @@ Here are some more examples to illustrate the use of rexe.
|
|
862
763
|
|
863
764
|
To output the result to stdout, you can either call `puts` or specify the `-op` option:
|
864
765
|
|
865
|
-
```
|
766
|
+
```bash
|
866
767
|
➜ ~ rexe puts 1 / 3.0
|
867
768
|
0.3333333333333333
|
868
769
|
```
|
869
770
|
|
870
771
|
or:
|
871
772
|
|
872
|
-
```
|
773
|
+
```bash
|
873
774
|
➜ ~ rexe -op 1 / 3.0
|
874
775
|
0.3333333333333333
|
875
776
|
```
|
876
777
|
|
877
778
|
Since `*` is interpreted by the shell, if we do multiplication, we need to quote the expression:
|
878
779
|
|
879
|
-
```
|
780
|
+
```bash
|
880
781
|
➜ ~ rexe -op '2 * 7'
|
881
782
|
14
|
882
783
|
```
|
883
784
|
|
884
|
-
Of course, if you put the `-op` in the `REXE_OPTIONS` environment variable,
|
885
|
-
you don't need to be explicit about the output:
|
785
|
+
Of course, if you put the `-op` in the `REXE_OPTIONS` environment variable, you don't need to be explicit about the output:
|
886
786
|
|
887
|
-
```
|
787
|
+
```bash
|
888
788
|
➜ ~ export REXE_OPTIONS=-op
|
889
789
|
➜ ~ rexe '2 * 7'
|
890
790
|
14
|
@@ -897,8 +797,10 @@ you don't need to be explicit about the output:
|
|
897
797
|
|
898
798
|
Output the contents of `ENV` using AwesomePrint [see footnote ^4 regarding ENV.to_s]:
|
899
799
|
|
900
|
-
```
|
800
|
+
```bash
|
901
801
|
➜ ~ rexe -oa ENV
|
802
|
+
```
|
803
|
+
```
|
902
804
|
{
|
903
805
|
...
|
904
806
|
"LANG" => "en_US.UTF-8",
|
@@ -914,7 +816,7 @@ Output the contents of `ENV` using AwesomePrint [see footnote ^4 regarding ENV.t
|
|
914
816
|
|
915
817
|
Show disk space used/free on a Mac's main hard drive's main partition:
|
916
818
|
|
917
|
-
```
|
819
|
+
```bash
|
918
820
|
➜ ~ df -h | grep disk1s1 | rexe -ml \
|
919
821
|
"x = split; puts %Q{#{x[4]} Used: #{x[2]}, Avail: #{x[3]}}"
|
920
822
|
91% Used: 412Gi, Avail: 44Gi
|
@@ -928,29 +830,29 @@ Show disk space used/free on a Mac's main hard drive's main partition:
|
|
928
830
|
|
929
831
|
Show the 3 longest file names of the current directory, with their lengths, in descending order:
|
930
832
|
|
931
|
-
```
|
833
|
+
```bash
|
932
834
|
➜ ~ ls | rexe -ml -op "%Q{[%4d] %s} % [length, self]" | sort -r | head -3
|
933
835
|
[ 50] Agoda_Booking_ID_9999999 49_–_RECEIPT_enclosed.pdf
|
934
836
|
[ 40] 679a5c034994544aab4635ecbd50ab73-big.jpg
|
935
837
|
[ 28] 2018-abc-2019-01-16-2340.zip
|
936
838
|
```
|
937
839
|
|
938
|
-
When you right align numbers using printf formatting, sorting the lines
|
939
|
-
alphabetically will result in sorting them numerically as well.
|
840
|
+
When you right align numbers using printf formatting, sorting the lines alphabetically will result in sorting them numerically as well.
|
940
841
|
|
941
842
|
----
|
942
843
|
|
943
844
|
#### Print yellow (trust me!):
|
944
845
|
|
945
|
-
This uses an [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code)
|
946
|
-
to output text to the terminal in yellow:
|
846
|
+
This uses an [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code) to output text to the terminal in yellow:
|
947
847
|
|
948
|
-
```
|
848
|
+
```bash
|
949
849
|
➜ ~ cowsay hello | rexe -me "print %Q{\u001b[33m}; puts to_a"
|
950
850
|
➜ ~ # or
|
951
851
|
➜ ~ cowsay hello | rexe -mb "print %Q{\u001b[33m}; puts self"
|
952
852
|
➜ ~ # or
|
953
853
|
➜ ~ cowsay hello | rexe "print %Q{\u001b[33m}; puts STDIN.read"
|
854
|
+
```
|
855
|
+
```
|
954
856
|
_______
|
955
857
|
< hello >
|
956
858
|
-------
|
@@ -966,13 +868,11 @@ to output text to the terminal in yellow:
|
|
966
868
|
|
967
869
|
#### More YouTube: Differentiating Success and Failure
|
968
870
|
|
969
|
-
Let's take the YouTube example from the "Loading Files" section further.
|
970
|
-
Let's have the video that loads be different for the success or failure
|
971
|
-
of the command.
|
871
|
+
Let's take the YouTube example from the "Loading Files" section further. Let's have the video that loads be different for the success or failure of the command.
|
972
872
|
|
973
873
|
If we put this in a load file (such as ~/.rexerc):
|
974
874
|
|
975
|
-
```
|
875
|
+
```ruby
|
976
876
|
def play(piece_code)
|
977
877
|
pieces = {
|
978
878
|
hallelujah: "https://www.youtube.com/watch?v=IUZEtVbJT5c&t=0m20s",
|
@@ -994,19 +894,17 @@ end
|
|
994
894
|
def play_result_by_exit_code
|
995
895
|
play_result(STDIN.read.chomp == '0')
|
996
896
|
end
|
997
|
-
|
998
897
|
```
|
999
898
|
|
1000
899
|
Then when we issue a command that succeeds, the Hallelujah Chorus is played [see footnote ^2]:
|
1001
900
|
|
1002
|
-
```
|
901
|
+
```bash
|
1003
902
|
➜ ~ uname; echo $? | rexe play_result_by_exit_code
|
1004
903
|
```
|
1005
904
|
|
1006
|
-
...but when the command fails, in this case, with an executable which is not found, it plays Rick Astley's
|
1007
|
-
"Never Gonna Give You Up":
|
905
|
+
...but when the command fails, in this case, with an executable which is not found, it plays Rick Astley's "Never Gonna Give You Up":
|
1008
906
|
|
1009
|
-
```
|
907
|
+
```bash
|
1010
908
|
➜ ~ uuuuu; echo $? | rexe play_result_by_exit_code
|
1011
909
|
```
|
1012
910
|
|
@@ -1016,7 +914,7 @@ Then when we issue a command that succeeds, the Hallelujah Chorus is played [see
|
|
1016
914
|
|
1017
915
|
Another formatting example...I wanted to reformat this source code...
|
1018
916
|
|
1019
|
-
```
|
917
|
+
```ruby
|
1020
918
|
'i' => Inspect
|
1021
919
|
'j' => JSON
|
1022
920
|
'J' => Pretty JSON
|
@@ -1026,12 +924,9 @@ Another formatting example...I wanted to reformat this source code...
|
|
1026
924
|
'y' => YAML
|
1027
925
|
```
|
1028
926
|
|
1029
|
-
...into something more suitable for my help text.
|
1030
|
-
Admittedly, the time it took to do this with rexe probably exceeded the time to do it manually,
|
1031
|
-
but it was an interesting exercise and made it easy to try different formats. Here it is, after
|
1032
|
-
copying the original text to the clipboard:
|
927
|
+
...into something more suitable for my help text. Admittedly, the time it took to do this with rexe probably exceeded the time to do it manually, but it was an interesting exercise and made it easy to try different formats. Here it is, after copying the original text to the clipboard:
|
1033
928
|
|
1034
|
-
```
|
929
|
+
```bash
|
1035
930
|
➜ ~ pbpaste | rexe -ml -op "sub(%q{'}, '-o').sub(%q{' =>}, %q{ })"
|
1036
931
|
-oi Inspect
|
1037
932
|
-oj JSON
|
@@ -1047,10 +942,9 @@ copying the original text to the clipboard:
|
|
1047
942
|
|
1048
943
|
#### Currency Conversion
|
1049
944
|
|
1050
|
-
I travel a lot, and when I visit a country for the first time I often get confused by the exchange rate.
|
1051
|
-
I put this in my `~/.rexerc`:
|
945
|
+
I travel a lot, and when I visit a country for the first time I often get confused by the exchange rate. I put this in my `~/.rexerc`:
|
1052
946
|
|
1053
|
-
```
|
947
|
+
```ruby
|
1054
948
|
# Conversion rate to US Dollars
|
1055
949
|
module Curr
|
1056
950
|
module_function
|
@@ -1061,10 +955,9 @@ module Curr
|
|
1061
955
|
end
|
1062
956
|
```
|
1063
957
|
|
1064
|
-
If I'm lucky enough to be at my computer when I need to do a conversion,
|
1065
|
-
for example, to find the value of 150 Malaysian ringits in US dollars, I can do this:
|
958
|
+
If I'm lucky enough to be at my computer when I need to do a conversion, for example, to find the value of 150 Malaysian ringits in US dollars, I can do this:
|
1066
959
|
|
1067
|
-
```
|
960
|
+
```bash
|
1068
961
|
➜ rexe git:(master) ✗ rexe puts 150 / Curr.myr
|
1069
962
|
36.76470588235294
|
1070
963
|
```
|
@@ -1076,10 +969,9 @@ Obviously rates will change over time, but this will give me a general idea, whi
|
|
1076
969
|
|
1077
970
|
#### Reformatting Grep Output
|
1078
971
|
|
1079
|
-
I was recently asked to provide a schema for the data in my `rock_books` accounting gem. `rock_books` data is intended to be very small in size, and no data base is used. Instead, the input data is parsed on every run, and reports generated on demand. However, there are data structures (actually class instances) in memory at runtime, and their classes inherit from `Struct`.
|
1080
|
-
The definition lines look like this one:
|
972
|
+
I was recently asked to provide a schema for the data in my `rock_books` accounting gem. `rock_books` data is intended to be very small in size, and no data base is used. Instead, the input data is parsed on every run, and reports generated on demand. However, there are data structures (actually class instances) in memory at runtime, and their classes inherit from `Struct`. The definition lines look like this one:
|
1081
973
|
|
1082
|
-
```
|
974
|
+
```ruby
|
1083
975
|
class JournalEntry < Struct.new(:date, :acct_amounts, :doc_short_name, :description, :receipts)
|
1084
976
|
```
|
1085
977
|
|
@@ -1091,7 +983,7 @@ lib/rock_books/documents/journal_entry.rb:
|
|
1091
983
|
|
1092
984
|
So this is what worked well for me:
|
1093
985
|
|
1094
|
-
```
|
986
|
+
```bash
|
1095
987
|
➜ ~ grep Struct **/*.rb | grep -v OpenStruct | rexe -ml -op \
|
1096
988
|
"a = \
|
1097
989
|
gsub('lib/rock_books/', '') \
|
@@ -1118,9 +1010,7 @@ types/acct_amount.rb class AcctAmount (:date, :code, :amount
|
|
1118
1010
|
types/journal_entry_context.rb class JournalEntryContext (:journal, :linenum, :line)
|
1119
1011
|
```
|
1120
1012
|
|
1121
|
-
Although there's a lot going on in this code,
|
1122
|
-
the vertical and horizontal alignments and spacing make the code
|
1123
|
-
straightforward to follow. Here's what it does:
|
1013
|
+
Although there's a lot going on in this code, the vertical and horizontal alignments and spacing make the code straightforward to follow. Here's what it does:
|
1124
1014
|
|
1125
1015
|
* grep the code base for `"Struct"`
|
1126
1016
|
* exclude references to `"OpenStruct"` with `grep -v`
|
@@ -1134,44 +1024,23 @@ straightforward to follow. Here's what it does:
|
|
1134
1024
|
|
1135
1025
|
### Conclusion
|
1136
1026
|
|
1137
|
-
Rexe is not revolutionary technology, it's just plumbing that removes parsing,
|
1138
|
-
formatting, and low level
|
1139
|
-
configuration from your command line so that you can focus on the high level
|
1140
|
-
task at hand.
|
1027
|
+
Rexe is not revolutionary technology, it's just plumbing that removes parsing, formatting, and low level configuration from your command line so that you can focus on the high level task at hand.
|
1141
1028
|
|
1142
|
-
When we consider a new piece of software, we usually think "what would this be
|
1143
|
-
helpful with now?". However, for me, the power of rexe is not so much what I can do
|
1144
|
-
with it in a single use case now, but rather what will I be able to do over time
|
1145
|
-
as I accumulate more experience and expertise with it.
|
1029
|
+
When we consider a new piece of software, we usually think "what would this be helpful with now?". However, for me, the power of rexe is not so much what I can do with it in a single use case now, but rather what will I be able to do over time as I accumulate more experience and expertise with it.
|
1146
1030
|
|
1147
|
-
I suggest starting to use rexe even for modest improvements in workflow, even
|
1148
|
-
if it doesn't seem compelling. There's a good chance that as you use it over
|
1149
|
-
time, new ideas will come to you and the workflow improvements will increase
|
1150
|
-
exponentially.
|
1031
|
+
I suggest starting to use rexe even for modest improvements in workflow, even if it doesn't seem compelling. There's a good chance that as you use it over time, new ideas will come to you and the workflow improvements will increase exponentially.
|
1151
1032
|
|
1152
|
-
A word of caution though --
|
1153
|
-
the complexity and difficulty of _sharing_ your rexe scripts across systems
|
1154
|
-
will be proportional to the extent to which you use environment variables
|
1155
|
-
and loaded files for configuration and shared code.
|
1156
|
-
Be responsible and disciplined in making this configuration and code as clean and organized as possible.
|
1033
|
+
A word of caution though -- the complexity and difficulty of _sharing_ your rexe scripts across systems will be proportional to the extent to which you use environment variables and loaded files for configuration and shared code. Be responsible and disciplined in making this configuration and code as clean and organized as possible.
|
1157
1034
|
|
1158
1035
|
----
|
1159
1036
|
|
1160
1037
|
#### Footnotes
|
1161
1038
|
|
1162
|
-
[
|
1163
|
-
https://github.com/thisredone/rb. I started using `rb` and thought of lots of
|
1164
|
-
other features I would like to have, so I started working on rexe.
|
1039
|
+
[1]: Rexe is an embellishment of the minimal but excellent `rb` script at https://github.com/thisredone/rb. I started using `rb` and thought of lots of other features I would like to have, so I started working on rexe.
|
1165
1040
|
|
1166
|
-
[
|
1167
|
-
You may need to change your default browser, or change the code that opens the URL.
|
1168
|
-
Firefox's new (as of March 2019) version 66 suppresses autoplay; you can register
|
1169
|
-
exceptions to this policy: open Firefox Preferences, search for "autoplay" and add
|
1170
|
-
"https://www.youtube.com".
|
1041
|
+
[2]: It's possible that when this page opens in your browser it will not play automatically. You may need to change your default browser, or change the code that opens the URL. Firefox's new (as of March 2019) version 66 suppresses autoplay; you can register exceptions to this policy: open Firefox Preferences, search for "autoplay" and add "https://www.youtube.com".
|
1171
1042
|
|
1172
|
-
[
|
1173
|
-
On Linux, `xdg-open` may not be installed by default. Also, Windows Subsystem for Linux (WSL)
|
1174
|
-
out of the box is not able to launch graphical applications.
|
1043
|
+
[3]: Making this truly OS-portable is a lot more complex than it looks on the surface. On Linux, `xdg-open` may not be installed by default. Also, Windows Subsystem for Linux (WSL) out of the box is not able to launch graphical applications.
|
1175
1044
|
|
1176
1045
|
Here is a _start_ at a method that opens a resource portably across operating systems:
|
1177
1046
|
|
@@ -1190,4 +1059,4 @@ Here is a _start_ at a method that opens a resource portably across operating sy
|
|
1190
1059
|
end
|
1191
1060
|
```
|
1192
1061
|
|
1193
|
-
[
|
1062
|
+
[4]: 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`.
|