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

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