Oils Reference — Chapter Word Language

This chapter describes the word language for OSH and YSH. Words evaluate to strings, or arrays of strings.

(in progress)

In This Chapter

Expressions to Words

expr-sub

Try to turn an expression into a string. Examples:

$ echo $[3 * 2]
6

$ var s = 'foo'
$ echo $[s[1:]]
oo

Some types can't be stringified, like Dict and List:

$ var d = {k: 42}

$ echo $[d]
fatal: expected Null, Bool, Int, Float, Eggex

You can explicitly use toJson8 or toJson():

$ echo $[toJson8(d)]
{"k":42}

(This is similar to json write (d))

expr-splice

Splicing puts the elements of a List into a string array context:

$ var foods = ['ale', 'bean', 'corn']
$ echo pizza @[foods[1:]] worm
pizza bean corn worm

This syntax is enabled by shopt --set parse_at, which is part of YSH.

var-splice

$ var foods = ['ale', 'bean', 'corn']
echo @foods

This syntax is enabled by shopt --set parse_at, which is part of YSH.

Formatting Typed Data as Strings

ysh-printf

Not done.

echo ${x %.3f}

ysh-format

Not done.

echo ${x|html}

Quotes

osh-string

TODO: elaborate

ysh-string

YSH strings in the word language are the same as in the expression language.

See ysh-string in chap-expr-lang.

triple-quoted

Triple-quoted in the word language are the same as in the expression language.

See triple-quoted in chap-expr-lang.

tagged-str

Not done.

Substitutions

command-sub

Executes a command and captures its stdout.

OSH has shell-compatible command sub like $(echo hi). If a trailing newline is returned, it's removed:

$ hostname
example.com

$ echo "/tmp/$(hostname)"
/tmp/example.com

YSH has spliced command subs, enabled by shopt --set parse_at. The result is a List of strings, rather than a single string.

$ write -- @(echo foo; echo 'with spaces')
foo
with-spaces

The command's stdout parsed as the "J8 Lines" format, where each line is either:

  1. An unquoted string, which must be valid UTF-8. Whitespace is allowed, but not other ASCII control chars.
  2. A quoted J8 string (JSON style "" or J8-style b'' u'' '')
  3. An ignored empty line

See J8 Notation for more details.

var-sub

Evaluates to the value of a variable:

$ x=X
$ echo $x ${x}
X X

arith-sub

Shell has C-style arithmetic:

$ echo $(( 1 + 2*3 ))
7

tilde-sub

Used as a shortcut for a user's home directory:

~/src     # my home dir
~bob/src  # user bob's home dir

proc-sub

Open stdout as a named file in /dev/fd, which can be passed to a command:

diff <(sort L.txt) <(sort R.txt)

Open stdin as a named file in /dev/fd:

seq 3 | tee >(sleep 1; tac)

Var Ops

There are three types of braced variable expansions:

${!name*} or ${!name@}
${!name[@]} or ${!name[*]}
${ops var ops}

name needs to be a valid identifier. If the expansion matches the first form, the variable names starting with name are generated. Otherwise, if the expansion matches the second form, the keys of the indexed or associative array named name are generated. When the expansion does not much either the first or second forms, it is interpreted as the third form of the variable name surrounded by operators.

op-bracket

The value within brackets is called an "index", and retrieves a value from an array:

${A[i+1]}
${A['key']}

If A is an indexed array, the index is interpreted as an arithmetic expression. Arithmetic evaluation is performed, and the value at that numeric offset is retrieved.

If A is an associative array, the index is interpreted as a string. The value associated with that string is retrieved.

If A is a string, it's treated as an indexed array with a single element, i.e. so that ${A[0]} is ${A}.


${A[*]}
${A[@]}

The index expressions [*] and [@] are special cases. Both generate a word list of all elements in a.

When the variable substitution is unquoted, there's no difference between [*] and [@]:

$ A=(1 2 3)
$ printf '<%s>\n' ${A[*]}
<1>
<2>
<3>

$ printf '<%s>\n' ${A[@]}
<1>
<2>
<3>

When double-quoted, the [*] form joins the elements by the first character of IFS:

$ IFS=x
$ printf '<%s>\n' "${A[*]}"
<1x2x3>

When double-quoted, the [@] form generates a word list by splitting the word at the boundary of every element in A:

$ printf '<%s>\n' "-${A[@]}-"
<-1>
<2>
<3->

If the container A has no elements, and the variable substitution has no other parts, [@] evaluates to an empty word list:

$ empty=()
$ set -- "${empty[@]}"
$ echo $#
0

These rules for [*] and [@] also apply to:

op-indirect

The indirection operator ! is a prefix operator, and it interprets the received string as a variable name name, an array element name[key], or an arrat list name[@] / name[*] and reads its values.

$ a=1234
$ v=a
$ echo $v
a
$ echo ${!v}
1234

op-test

Shell has boolean operations within ${}. I use :- most frequently:

x=${1:-default}
osh=${OSH:-default}

This idiom is also useful:

: ${LIB_OSH=stdlib/osh}

There are test operators with colons, and without:

${x-default}
${x:-default}

${x=default}
${x:=default}

${x+other}
${x:+other}

${x?error}
${x:?error}

Without the colon, the shell checks whether a value is defined. In the case of a word list, e.g. generated by $* or $@, it tests whether there is at least one element.

With the colon, the shell checks whether the value is non-empty (is not the empty string). In the case of a word list, the test is performed after joining the elements by a space.

Elements are joined by the first character of IFS only with double-quoted "${*:-}".

In contrast, ${*:-}, ${@:-}, and "${@:-}" are joined by a space. This is because the joining of "$*" by IFS is performed earlier than the joining by space for the test.

op-strip

Remove prefixes or suffixes from strings:

echo ${y#prefix}
echo ${y##'prefix'}

echo ${y%suffix}
echo ${y%%'suffix'}

The prefix and suffix can be glob patterns, but this usage is discouraged because it may be slow.

op-patsub

Replace a substring or pattern.

The character after the first / can be / to replace all occurrences:

$ x=food

$ echo ${x//o/--}      # replace 1 o with 2 --
f----d

It can be # or % for an anchored replacement:

$ echo ${x/#f/--}      # left anchored f
--ood

$ echo ${x/%d/--}      # right anchored d
foo--

The pattern can also be a glob:

$ echo ${x//[a-z]/o}   # replace 1 char with o
oooo

$ echo ${x//[a-z]+/o}  # replace multiple chars
o

op-slice

echo ${a[@]:1:2}
echo ${@:1:2}

op-format

${x@P} evaluates x as a prompt string, i.e. the string that would be printed if PS1=$x.


${x@Q} quotes the value of x, if necessary, so that it can be evaluated as a shell word.

$ x='<'
$ echo "value = $x, quoted = ${x@Q}."
value = <, quoted = '<'.

$ x=a
$ echo "value = $x, quoted = ${x@Q}."
value = a, quoted = a.

In the second case, the string a doesn't need to be quoted.


Format operations like @Q generally treat empty variables differently than unset variables.

That is, ${empty@Q} is the string '', while ${unset@Q} is an empty string:

$ x=''
$ echo "value = $x, quoted = ${x@Q}."
value = , quoted = ''.

$ unset -v x
$ echo "value = $x, quoted = ${x@Q}."
value = , quoted = .

${x@a} returns characters that represent the attributes of the ${x}, or more precisely, the h-value of ${x}.

Definitions:

For example, with arr=(1 2 3):

Reference Expression H-value R-value Flags returned
${arr[0]@a} or
${arr@a}
array
(1 2 3)
string
1
a
${arr[@]@a} array
(1 2 3)
array
(1 2 3)
a a a
ref=arr or ref=arr[0] ${!ref@a} array
(1 2 3)
string
1
a
ref=arr[@] ${!ref@a} array
(1 2 3)
array
(1 2 3)
a a a

When ${x} would result in a word list, ${x@a} returns a word list containing the attributes of the h-value of each word.


These characters may be returned:

Character Where ${x} would be obtained
a indexed array
A associative array
r readonly container
x exported variable
n name reference (OSH extension)
Generated on Sun, 05 Jan 2025 23:28:55 -0500