source | all docs for version 0.8.0 | all versions | oilshell.org
This is an informal, lightly-organized list of recommended idioms for the Oil language. Each section has snippets labeled No and Yes.
No:
local x='my song.mp3'
ls "$x" # quotes required to avoid mangling
Yes:
var x = 'my song.mp3'
ls $x # no quotes needed
No:
local myflags=( --all --long )
ls "${myflags[@]}" "$@"
Yes:
var myflags = %( --all --long )
ls @myflags @ARGV
No:
local maybe_empty=''
cp $maybe_empty other_file $dest # omitted if empty
Yes:
var e = ''
cp @maybe(e) other_file $dest
No:
local packages='python-dev gawk'
apt install $packages
Yes:
var packages = 'python-dev gawk'
apt install @split(packages)
Even better:
var packages = %(python-dev gawk) # array literal
apt install @packages # splice array
No:
local n=3
for x in $(seq $n); do # No implicit splitting of unquoted words in Oil
echo $x
done
Yes:
var n = 3
for x in @(seq $n) { # Explicit splitting
echo $x
}
Note that {1..3}
works in bash and Oil, but the numbers must be constant.
No:
local pat='*.py'
echo $pat
Yes:
var pat = '*.py'
echo @glob(pat)
In other words, avoid groveling through backslashes and spaces in shell.
Instead, emit and consume the QSN and QTSV interchange formats.
Custom parsing and serializing should be limited to "the edges" of your Oil programs.
These are discussed in the next two sections, but here's a summary.
write --qsn # also -q
read --qsn :mystr # also -q
read --line --qsn :myline # read a single line
read --lines --qsn :myarray # read many lines
That is, take advantage of the the invariants that the IO builtins respect. (doc in progress)
TODO: Implement and test these.
proc
construct and flagspec
!--qsn
flag.ls
in Python with
little effort.write
Builtin Is Simpler Than printf
and echo
No:
printf '%s\n' "$mystr"
Yes:
write -- $mystr
The write
builtin accepts --
so it doesn't confuse flags and args.
No:
echo -n "$mystr" # breaks if mystr is -e
Yes:
write --end '' -- $mystr
write -n -- $mystr # -n is an alias for --end ''
var myarray = %(one two three)
write -- @myarray
read
builtinNo:
read line # Bad because it mangles your backslashes!
read -r line # Better, but easy to forget
Yes:
read --line # also faster because it's a buffered read
TODO: implement this.
No:
mapfile -d ''
read -d ''
Yes:
read --all :mystr
TODO: implement this.
No:
( cd /tmp; echo $PWD ) # subshell is unnecessary (and limited)
No:
pushd /tmp
echo $PWD
popd
Yes:
cd /tmp {
echo $PWD
}
No:
set +o errexit
myfunc # without error checking
set -o errexit
Yes:
shopt --unset errexit {
myfunc
}
TODO: Implement this.
forkwait
builtin for Subshells, not ()
No:
( not_mutated=foo )
echo $not_mutated
Yes:
forkwait {
setvar not_mutated = 'foo'
}
echo $not_mutated
TODO: Implement this.
fork
builtin for async, not &
No:
myproc &
{ sleep 1; echo one; sleep 2; } &
Yes:
fork myproc
fork { sleep 1; echo one; sleep 2 }
TODO: Implement this.
No:
f() {
local src=$1
local dest=${2:-/tmp}
cp "$src" "$dest"
}
Yes:
proc f(src, dest='/tmp') { # Python-like default values
cp $src $dest
}
No:
f() {
local first=$1
shift
echo $first
echo "$@"
}
Yes:
proc f(first, @rest) { # @ means "the rest of the arguments"
write -- $first
write -- @rest # @ means "splice this array"
}
TODO: Test this out.
No:
f() {
local in=$1
local -n out=$2
out=PREFIX-$in
}
myvar='zzz'
f zzz myvar # assigns myvar to 'PREFIX-zzz'
Yes:
proc f(in, :out) { # : means accept a string "reference"
setref out = "PREFIX-$in"
}
var myvar = 'zzz'
f zzz :myvar # : means pass a string "reference" (optional)
TODO: Test this out.
Shell functions can access variables in their caller:
foo() {
foo_var=x;
bar
}
bar() {
echo $foo_var # looks up the stack
}
foo
In Oil, you have to pass params explicitly:
proc bar {
echo $foo_var # error
}
errexit
Bug in POSIX shell:
if myfunc; then # oops, errors not checked in myfunc
echo hi
fi
Suggested workaround:
if $0 myfunc; then # invoke a new shell
echo hi
fi
"$@"
Oil extension, without an extra process:
if invoke myfunc; then
echo hi
fi
Even better:
if myfunc { # implicit 'invoke', equivalent to the above
echo hi
}
Note that &&
and ||
and !
require an explicit invoke
:
No:
myfunc || fail
myfunc && echo 'success'
! myfunc
Yes:
invoke myfunc || fail
invoke myfunc && echo 'success'
! invoke myfunc
This explicit syntax avoids breaking POSIX shell. You have to opt in to the better behavior.
TODO: Implement this.
No:
local mystr=foo
mystr='new value'
local myint=42 # still a string in shell
Yes:
var mystr = 'foo'
setvar mystr = 'new value'
var myint = 42 # a real integer
No:
x=$(( 1 + 2*3 ))
(( x = 1 + 2*3 ))
Yes:
setvar x = 1 + 2*3
No:
(( i++ )) # interacts poorly with errexit
i=$(( i+1 ))
Yes:
setvar i += 1 # like Python, with a keyword
No:
local -a myarray=(one two three)
myarray[3]='THREE'
local -A myassoc=(['key']=value ['k2']=v2)
myassoc['key']=V
Yes:
var myarray = %(one two three)
setvar myarray[3] = 'THREE'
# keys don't need to be quoted
var myassoc = %{key: 'value', k2: 'v2'}
setvar myassoc['key'] = 'V'
Container literals start with the %
sigil. (TODO: Implement this. It's @
right now.)
No:
local x=${a[i-1]}
x=${a[i]}
local y=${A['key']}
Yes:
var x = a[i-1]
setvar x = a[i]
var y = A['key']
No:
if (( x > 0 )); then
echo positive
fi
Yes:
if (x > 0) {
echo 'positive'
}
No:
echo flag=$((1 + a[i] * 3)) # C-like arithmetic
Yes:
echo flag=$[1 + a[i] * 3] # Arbitrary Oil expressions
# Possible, but a local var might be more readable
echo flag=$['1' if x else '0']
No:
local pat='[[:digit:]]+'
if [[ $x =~ $pat ]]; then
echo 'number'
fi
Yes:
if (x ~ /digit+/) {
echo 'number'
}
Or extract the pattern:
var pat = / digit+ /
if (x ~ pat) {
echo 'number'
}
TODO: BASH_REMATCH
alternative.
No:
if [[ $x == *.py ]]; then
echo Python
fi
TODO: Implement the ~~
operator.
Yes:
if (x ~~ '*.py') {
echo 'Python'
}
No:
case $x in
*.py)
echo Python
;;
*.sh)
echo Shell
;;
esac
Yes (purely a style preference):
case $x { # curly braces
(*.py) # balanced parens
echo 'Python'
;;
(*.sh)
echo 'Shell'
;;
}
--long-flags
for builtinsEasier to write:
test -d /tmp
test -d / -a -f /vmlinuz
shopt -u extglob
Easier to read:
test --dir /tmp
test --dir / && test --file /vmlinuz
shopt --unset extglob
Style note: Prefer test
to [
, because idiomatic Oil code doesn't use
"puns".
TODO: implement this.
TODO