Why Sponsor Oil? | source | all docs for version 0.14.2 | all versions | oilshell.org
Warning: Work in progress! Leave feedback on Zulip or Github if you'd like this doc to be updated.
Recall that Oil is composed of three interleaved languages: words, commands, and expressions.
This doc describes words, but only the things that are not in:
#word-lang
section of OSH Help
Topics#word-lang
section of Oil Help
TopicsA word is an expression like $x
, "hello $name"
, or {build,test}/*.py
. It
evaluates to a string or an array of strings.
Generally speaking, Oil behaves like a simpler version of POSIX shell / bash. Sophisticated users can read Simple Word Evaluation for a comparison.
Part of an expression:
var x = ${y:-'default'}
Part of a command:
echo ${y:-'default'}
The three contexts where splitting and globbing apply are the ones where a
sequence of words is evaluated (EvalWordSequence
):
echo $x foo
for i in $x foo; do ...
a=($x foo)
and var a = %($x foo)
(oil-array)Oil has a new array syntax, but it also supports the bash-compatible syntax:
local myarray=(one two *.py) # bash
var myarray = %(one two *.py) # Oil style
Shell also has contexts where it evaluates words to a single string, rather than a sequence, like:
# RHS of Assignment
x="${not_array[@]}"
x=*.py # not a glob
# Redirect Arg
echo foo > "${not_array[@]}"
echo foo > *.py # not a glob
# Case variables and patterns
case "${not_array1[@]}" in
"${not_array2[@]}")
echo oops
;;
esac
case *.sh in # not a glob
*.py) # a string pattern, not a file system glob
echo oops
;;
esac
The behavior of these snippets diverges a lot in existing shells. That is, shells are buggy and poorly-specified.
Oil disallows most of them. Arrays are considered separate from strings and don't randomly "decay".
Related: the RHS of an Oil assignment is an expression, which can be of any type, including an array:
var parts = split(x) # returns an array
var python = glob('*.py') # ditto
var s = join(parts) # returns a string
This is a recap of A Feel for Oil's Syntax.
$
Means "Returns One String"Examples:
All substitutions: var, command, arith
$[a[x+1]]
as an expression substitution?$[ /pat+ /]
?Inline function calls, an Oil extension: $join(myarray)
(C-style strings like $'\n'
use $
, but that's more of a bash anachronism.
In Oil, c'\n'
is preferred.
@
Means "Returns An Array of Strings"Enabled with shopt -s parse_at
.
Examples:
@myarray
@arrayfunc(x, y)
These are both Oil extensions.
The array literal syntax also uses a @
:
var myarray = %(1 2 3)
This feature is purely syntactic sugar. Instead of:
write $strfunc(x) @arrayfunc(y)
You can always refactor to:
var mystr = strfunc(x)
var myarray = arrayfunc(y)
write $mystr @myarray
Examples:
echo $join(myarray, '/')
echo $len(mystr) # len returns an int, but it's automatically converted to a string
echo foo=$len(mystr) # also works
Note that inline function calls can't be placed in double quoted strings:
"__$len(s)__"
You can either extract a variable:
var x = len(s)
echo "__$x__"
or use an expression substitution (expr-sub):
echo $[len(x)]
$[]
is for Oil expressions, while ${}
is shell.
This is documented in warts.
cc -o foo -- @arrayfunc(x, y)
echo @split(mystr, '/') # split on a delimiter
Uses POSIX behavior for unquoted substitutions like $x
.
$IFS
.Shell has odd "joining" semantics, which are supported in Oil but generally discouraged:
set -- 'a b' 'c d'
argv.py X"$@"X # => ['Xa', 'b', 'c', 'dX']
In Oil, the RHS of an assignment is an expression, and joining only occurs within double quotes:
# Oil
var joined = $x$y # parse error
var joined = "$x$y" # OK
# Shell
joined=$x$y # OK
joined="$x$y" # OK
Extended globs in OSH are a "legacy syntax" modelled after the behavior of
bash
and mksh
. This features adds alternation, repetition, and negation to
globs, giving the power of regexes.
You can use them to match strings:
$ [[ foo.cc == *.(cc|h) ]] && echo 'matches' # => matches
Or produce lists of filename arguments:
$ touch foo.cc foo.h
$ echo *.@(cc|h) # => foo.cc foo.h
There are some limitations and differences:
FNM_EXTMATCH
extension to fnmatch()
. Unlike bash and
mksh, Oil doesn't implement its own extended glob matcher.mksh
. When an extended glob appears in a
word, we evaluate the word, match filenames, and skip the rest of the
word evaluation pipeline. This means:
$unquoted/@(*.cc|h)
."$@"
and extended globs in the same word, e.g.
"$@"_*.@(cc|h)
. This is usually nonsensical anyway.echo foo > @(cc|h)
is a runtime error in OSH, but other
shells will write a file literally named @(cc|h)
.${undef:-@(cc)}
. But it does accept ${x%@(cc)}
,
since string strip operators like %
accept a glob.shopt -s extglob
.
bash
can't parse some extended globs unless extglob
is on. But
it parses others when it's off.PATTERN
in ${x//PATTERN/replace}
.
This is because we only translate normal (non-extended) globs to regexes (in
order to get the position information necessary for string replacement).shopt --set simple_word_eval
(Oil word
evaluation).
This is the same discussion as $f(x) vs
$(f(x))` on the inline function
calls
thread.
We only want to interpolate vars and functions. Arbitrary expressions aren't necessary.
In summary:
echo foo=$x
interpolates a variable into a unquoted wordecho foo=$f(x)
interpolates a call returning a string into an unquoted wordecho "foo=$[x] 1 2 3"
interpolates a variable into a double quoted stringecho "foo=${x} 1 2 3"
-- older, sameecho "foo=$[f(x)] 1 2 3"
interpolates a call returning a string into a
double quoted stringAnd then for completeness we also have:
echo @x
interpolates an array into a commandecho @f(x)
interpolates a function returning an array into a command