Why Sponsor Oils? | blog | oilshell.org
Update 10/2019: The Oil language has changed. This post is accurate in spirit but not in detail.
In yesterday's post, we looked at the translation of make-hdb.sh and the oil language features it uses. Today we continue with three more translations.
Make sure to open the source files in a new window, and widen it so that the two code panes appear side-by-side.
I explained the [
to test
conversion yesterday, so the only new thing here
is subshell syntax. Subshells look like this:
shell {
cd packages/busybox
echo "Working out of $PWD"
git pull
}
Again, all blocks use the same curly brace syntax, rather than having a mix of
{ }
, ( )
, do done
, and then fi
.
Additionally, I plan to add a block argument to cd
for this common idiom,
in the style of Ruby:
cd packages/busybox {
echo "Working out of $PWD"
git pull
} # working directory restored on block exit; no need to fork()
This is nicer than a pushd
/ popd
pair in bash, especially in the
presence of exceptions. A non-zero exit code under set -o errexit
is
essentially an uncatchable exception in shell. Oil will allow you to catch
these errors.
Notice the following:
(1) Mutable global variables require the global
keyword. Shell has the
anachronism that variables are global by default, unless declared with local
keyword. In Oil, local variables are declared with var
or const
.
(2) Initialization uses the =
operator, but mutation uses :=
. Unlike
shell and Python, variables aren't created automatically upon assignment.
They're initialized once and then used multiple times.
The semantics are somewhat like ES6, but I'm not sure whether there will be block scope.
(3) .
(period) is not an alias for source
. Again, oil prefers readable
names. Auto-completion makes it easier to type such names.
(4) case is spelled matchstr
and uses curly braces. The match
keyword
is reserved for a construct that will work on arbitrary types like integers,
strings, and structured data.
The code looks messy because the conversion intentionally preserves formatting. I prefer it written like this:
matchstr $1 {
'start' { # No-op
}
'restart' | 'reload' | 'force-reload' {
echo "Error: argument '$1' not supported" > !2
exit 3
}
'stop' {
do_stop
}
* {
echo "Usage: $0 start|stop" > !2
exit 3
}
}
(5) File descriptors are prefixed with !. Shell has a bad syntax for redirects:
echo 'to stderr' 1>&2 # Valid
echo 'to stderr' 1>& 2 # Valid space after >&
echo 'to stderr' 1 >&2 # INVALID space before >&
That is, >
and >&
are actually unary prefix operators, but there's
lexical hack to accept an optional descriptor to their left. This makes them
seem like binary operators with quirky rules about whitespace.
In oil, file descriptors have their own syntax to distinguish them from words:
echo 'to stderr' > !2 # write to stderr, like >&2
echo 'to stderr' !1 > !2 # same thing, like 1>&2
echo 'to stderr' !2 > stderr.txt
cat < stdin.txt # stdin from file
cat !0 < stdin.txt # same thing, like 0< stdin.txt
The !
syntax also leaves room for named file descriptors.
This longer script makes use of many constructs we've seen: proc
, if
,
matchstr
, test
, source
, shell
, and global
.
Proponents of systemd argue that init shell scripts are poorly written, and there's evidence of that here.
In the run_by_init
function, the subshell construct ()
is confused with the
grouping construct {
}
. I've seen this many times, and it's in part due to the fact that ()
are proper operators and easier to use without spaces:
(echo hi) # valid
( echo hi ) # valid
( echo hi; ) # valid
{echo hi} # INVALID, tries to run the '{echo' command
{ echo hi } # INVALID, tries to print 'hi }'
{ echo hi; } # valid. Space after { and terminating ; are required.
In Problems with $((, I mentioned that all five
occurrences I found of a subshell ()
within a command substitution $()
were
improper usages! They should have used grouping { }
within a command
substitution $()
.
Syntax clearly matters, and I hope that oil's syntax will be easy to learn for
Also recall what I mentioned yesterday: a new syntax will not only be easier to learn, but it will also enable new features. Shell needs new features. In a decade where it's been almost entirely static, languages like Python and JavaScript have evolved tremendously.