OILS / doc / ref / chap-word-lang.md View on Github | oils.pub

546 lines, 363 significant
1---
2title: Word Language (Oils Reference)
3all_docs_url: ..
4body_css_class: width40
5default_highlighter: oils-sh
6preserve_anchor_case: yes
7---
8
9<div class="doc-ref-header">
10
11[Oils Reference](index.html) &mdash;
12Chapter **Word Language**
13
14</div>
15
16This chapter describes the word language for OSH and YSH. Words evaluate to
17strings, or arrays of strings.
18
19<span class="in-progress">(in progress)</span>
20
21<div id="dense-toc">
22</div>
23
24<h2 id="expression">Expressions to Words</h2>
25
26### expr-sub
27
28Try to turn an expression into a string. Examples:
29
30 $ echo $[3 * 2]
31 6
32
33 $ var s = 'foo'
34 $ echo $[s[1:]]
35 oo
36
37Some types can't be stringified, like Dict and List:
38
39 $ var d = {k: 42}
40
41 $ echo $[d]
42 fatal: expected Null, Bool, Int, Float, Eggex
43
44You can explicitly use `toJson8` or `toJson()`:
45
46 $ echo $[toJson8(d)]
47 {"k":42}
48
49(This is similar to `json write (d)`)
50
51### expr-splice
52
53Splicing puts the elements of a `List` into a string array context:
54
55 $ var foods = ['ale', 'bean', 'corn']
56 $ echo pizza @[foods[1:]] worm
57 pizza bean corn worm
58
59This syntax is enabled by `shopt --set` [parse_at][], which is part of YSH.
60
61[parse_at]: chap-option.html#ysh:upgrade
62
63### var-splice
64
65 $ var foods = ['ale', 'bean', 'corn']
66 echo @foods
67
68This syntax is enabled by `shopt --set` [parse_at][], which is part of YSH.
69
70
71<h2 id="formatting">Formatting Typed Data as Strings</h2>
72
73### ysh-printf
74
75Not done.
76
77 echo ${x %.3f}
78
79### ysh-format
80
81Not done.
82
83 echo ${x|html}
84
85## Joining
86
87### osh-word-join
88
89OSH joins arbitrary word parts:
90
91 $ myvar=/
92 $ echo 'single'\'$myvar"double $myvar"
93 single'/double /
94
95That is, the argument to `echo` is a word that has 4 word parts:
96
97 'single'
98 \'
99 $myvar
100 "double $myvar"
101
102When the word is evaluated, the result of each part is evaluated and
103concatenated.
104
105### ysh-word-join
106
107In general, YSH doesn't allow single- and double-quoted parts to be joined.
108They most often form an entire word:
109
110 echo 'single-quoted word'
111 echo "double-quoted word"
112
113YSH allows word joining in these special cases:
114
115 echo --flag='value'
116 echo NAME="value"
117
118 echo ~/'dir with spaces'
119 echo ~root/src/"dir with spaces"
120
121The purpose of this rule is to eliminate ambiguous words like:
122
123 --flag=u'value\n' # does u indicate a J8 string, or is it a literal?
124
125See [OILS-ERR-17][] for details.
126
127[OILS-ERR-17]: ../error-catalog.html#oils-err-17
128
129## Quotes
130
131
132### osh-string
133
134- Single quotes
135- Double Quotes
136- C-style strings: `$'\n'`
137
138TODO: elaborate
139
140### ysh-string
141
142YSH strings in the word language are the same as in the expression language.
143
144See [ysh-string in chap-expr-lang](chap-expr-lang.html#ysh-string).
145
146### triple-quoted
147
148Triple-quoted in the word language are the same as in the expression language.
149
150See [triple-quoted in chap-expr-lang](chap-expr-lang.html#triple-quoted).
151
152### tagged-str
153
154Not done.
155
156## Substitutions
157
158### command-sub
159
160Executes a command and captures its stdout.
161
162If stdout has a trailing newline, it's removed:
163
164 $ hostname
165 example.com
166
167 $ echo "/tmp/$(hostname)"
168 /tmp/example.com
169
170If stdout has any NUL bytes, they are removed (regardless of position).
171
172Related: [captureStdout()](chap-type-method.html#captureStdout)
173
174### ysh-command-sub
175
176YSH also has spliced command subs, enabled by `shopt --set parse_at`. The
177result is a **List** of strings, rather than a single string.
178
179 $ write -- @(echo foo; echo 'with spaces')
180 foo
181 with-spaces
182
183The command's stdout parsed as the "J8 Lines" format, where each line is
184either:
185
1861. An unquoted string, which must be valid UTF-8. Whitespace is allowed, but
187 not other ASCII control chars.
1882. A quoted J8 string (JSON style `""` or J8-style `b'' u'' ''`)
1893. An **ignored** empty line
190
191See [J8 Notation](../j8-notation.html) for more details.
192
193### var-sub
194
195Evaluates to the value of a variable:
196
197 $ x=X
198 $ echo $x ${x}
199 X X
200
201### arith-sub
202
203Shell has C-style arithmetic:
204
205 $ echo $(( 1 + 2*3 ))
206 7
207
208### tilde-sub
209
210Used as a shortcut for a user's home directory:
211
212 ~/src # my home dir
213 ~bob/src # user bob's home dir
214
215### proc-sub
216
217Open stdout as a named file in `/dev/fd`, which can be passed to a command:
218
219 diff <(sort L.txt) <(sort R.txt)
220
221Open stdin as a named file in `/dev/fd`:
222
223 seq 3 | tee >(sleep 1; tac)
224
225
226## Var Ops
227
228There are three types of braced variable expansions:
229
230 ${!name*} or ${!name@}
231 ${!name[@]} or ${!name[*]}
232 ${ops var ops}
233
234`name` needs to be a valid identifier. If the expansion matches the first
235form, the variable names starting with `name` are generated. Otherwise, if the
236expansion matches the second form, the keys of the indexed or associative array
237named `name` are generated. When the expansion does not much either the first
238or second forms, it is interpreted as the third form of the variable name
239surrounded by operators.
240
241
242### op-bracket
243
244The value within brackets is called an "index", and retrieves a value from an
245array:
246
247 ${A[i+1]}
248 ${A['key']}
249
250If `A` is an indexed array, the index is interpreted as an arithmetic
251expression. Arithmetic evaluation is performed, and the value at that numeric
252offset is retrieved.
253
254If `A` is an associative array, the index is interpreted as a string. The
255value associated with that string is retrieved.
256
257If `A` is a string, it's treated as an indexed array with a single element,
258i.e. so that `${A[0]}` is `${A}`.
259
260---
261
262 ${A[*]}
263 ${A[@]}
264
265The index expressions `[*]` and `[@]` are special cases. Both generate a word
266list of all elements in `a`.
267
268When the variable substitution is **unquoted**, there's no difference between
269`[*]` and `[@]`:
270
271 $ A=(1 2 3)
272 $ printf '<%s>\n' ${A[*]}
273 <1>
274 <2>
275 <3>
276
277 $ printf '<%s>\n' ${A[@]}
278 <1>
279 <2>
280 <3>
281
282When double-quoted, the `[*]` form joins the elements by the first character of
283`IFS`:
284
285 $ IFS=x
286 $ printf '<%s>\n' "${A[*]}"
287 <1x2x3>
288
289When double-quoted, the `[@]` form generates a word list by splitting the word
290at the boundary of every element in `A`:
291
292 $ printf '<%s>\n' "-${A[@]}-"
293 <-1>
294 <2>
295 <3->
296
297If the container `A` has no elements, and the variable substitution has no
298other parts, `[@]` evaluates to an empty word list:
299
300 $ empty=()
301 $ set -- "${empty[@]}"
302 $ echo $#
303 0
304
305---
306
307These rules for `[*]` and `[@]` also apply to:
308
309- `$*` and `$@`
310- `${!name*}` and `${!name@}`
311- `${!name[*]}` and `${!name[@]}`, etc.
312
313<!--
314Note: OSH currently joins the values by `IFS` even for unquoted `$*` and
315performs word splitting afterward. This is different from the POSIX standard.
316-->
317
318### op-indirect
319
320The indirection operator `!` is a prefix operator, and it interprets the
321received string as a variable name `name`, an array element `name[key]`, or an
322arrat list `name[@]` / `name[*]` and reads its values.
323
324 $ a=1234
325 $ v=a
326 $ echo $v
327 a
328 $ echo ${!v}
329 1234
330
331### op-test
332
333Shell has boolean operations within `${}`. I use `:-` most frequently:
334
335 x=${1:-default}
336 osh=${OSH:-default}
337
338This idiom is also useful:
339
340 : ${LIB_OSH=stdlib/osh}
341
342---
343
344There are test operators with colons, and without:
345
346 ${x-default}
347 ${x:-default}
348
349 ${x=default}
350 ${x:=default}
351
352 ${x+other}
353 ${x:+other}
354
355 ${x?error}
356 ${x:?error}
357
358**Without** the colon, the shell checks whether a value is **defined**. In the
359case of a word list, e.g. generated by `$*` or `$@`, it tests whether there is
360at least one element.
361
362**With** the colon, the shell checks whether the value is **non-empty** (is not
363the empty string). In the case of a word list, the test is performed after
364joining the elements by a space.
365
366Elements are joined by the first character of `IFS` only with double-quoted
367`"${*:-}"`.
368
369In contrast, `${*:-}`, `${@:-}`, and `"${@:-}"` are joined by a space. This is
370because the joining of `"$*"` by `IFS` is performed earlier than the joining by
371space for the test.
372
373<!--
374Note: OSH currently joins the values by `IFS` even for unquoted `$*`. This is
375different from Bash.
376-->
377
378### op-strip
379
380Remove prefixes or suffixes from strings:
381
382 echo ${y#prefix}
383 echo ${y##'prefix'}
384
385 echo ${y%suffix}
386 echo ${y%%'suffix'}
387
388The prefix and suffix can be glob patterns, but this usage is discouraged
389because it may be slow.
390
391### op-patsub
392
393Replace a substring or pattern.
394
395The character after the first `/` can be `/` to replace all occurrences:
396
397 $ x=food
398
399 $ echo ${x//o/--} # replace 1 o with 2 --
400 f----d
401
402It can be `#` or `%` for an anchored replacement:
403
404 $ echo ${x/#f/--} # left anchored f
405 --ood
406
407 $ echo ${x/%d/--} # right anchored d
408 foo--
409
410The pattern can also be a glob:
411
412 $ echo ${x//[a-z]/o} # replace 1 char with o
413 oooo
414
415 $ echo ${x//[a-z]+/o} # replace multiple chars
416 o
417
418### op-slice
419
420 echo ${a[@]:1:2}
421 echo ${@:1:2}
422
423### op-format
424
425${x@P} evaluates x as a prompt string, i.e. the string that would be printed if
426PS1=$x.
427
428---
429
430`${x@Q}` quotes the value of `x`, if necessary, so that it can be evaluated as
431a shell word.
432
433 $ x='<'
434 $ echo "value = $x, quoted = ${x@Q}."
435 value = <, quoted = '<'.
436
437 $ x=a
438 $ echo "value = $x, quoted = ${x@Q}."
439 value = a, quoted = a.
440
441In the second case, the string `a` doesn't need to be quoted.
442
443---
444
445Format operations like `@Q` generally treat **empty** variables differently
446than **unset** variables.
447
448That is, `${empty@Q}` is the string `''`, while `${unset@Q}` is an empty
449string:
450
451 $ x=''
452 $ echo "value = $x, quoted = ${x@Q}."
453 value = , quoted = ''.
454
455 $ unset -v x
456 $ echo "value = $x, quoted = ${x@Q}."
457 value = , quoted = .
458
459---
460
461`${x@a}` returns characters that represent the attributes of the `${x}`, or
462more precisely, the *h-value* of `${x}`.
463
464Definitions:
465
466- *h-value* is the variable (or the object that the variable directly points)
467 from which the result of `${x}` would originally come.
468- *r-value* is the value of the expansion of `${x}`
469
470For example, with `arr=(1 2 3)`:
471
472<style>
473table {
474 width: 100%;
475 margin-left: 2em; /* matches p text in manual.css */
476}
477thead {
478 text-align: left;
479}
480</style>
481
482<table>
483
484- thead
485 - Reference
486 - Expression
487 - H-value
488 - R-value
489 - Flags returned
490- tr
491 - <!-- empty -->
492 - `${arr[0]@a}` or <br/> `${arr@a}`
493 - array<br/> `(1 2 3)`
494 - string<br/> `1`
495 - `a`
496- tr
497 - <!-- empty -->
498 - `${arr[@]@a}`
499 - array<br/> `(1 2 3)`
500 - array<br/> `(1 2 3)`
501 - `a a a`
502- tr
503 - `ref=arr` or `ref=arr[0]`
504 - `${!ref@a}`
505 - array<br/> `(1 2 3)`
506 - string<br/> `1`
507 - `a`
508 - <!-- empty -->
509- tr
510 - `ref=arr[@]`
511 - `${!ref@a}`
512 - array<br/> `(1 2 3)`
513 - array<br/> `(1 2 3)`
514 - `a a a`
515
516</table>
517
518When `${x}` would result in a word list, `${x@a}` returns a word list
519containing the attributes of the *h-value* of each word.
520
521---
522
523These characters may be returned:
524
525<table>
526
527- thead
528 - Character
529 - Where `${x}` would be obtained
530- tr
531 - `a`
532 - indexed array
533- tr
534 - `A`
535 - associative array
536- tr
537 - `r`
538 - readonly container
539- tr
540 - `x`
541 - exported variable
542- tr
543 - `n`
544 - name reference (OSH extension)
545
546</table>