source | all docs for version 0.8.10 | all versions | oilshell.org
This doc addresses these questions:
The Oil language is a graceful upgrade to shell, and the behavior of variables follows from that philosophy.
Oil has 5 keywords affect shell variables. Unlike shell builtins, they're statically-parsed, and take dynamically-typed expressions on the right.
var and constThis is similar to JavaScript.
proc p {
var name = 'Bob'
const age = (20 + 1) * 2
echo "$name is $age years old" # Bob is 42 years old
}
setvar and setglobalproc p {
var name = 'Bob' # declare
setvar name = 'Alice' # mutate
setglobal g = 42 # create or mutate a global variable
}
setref (advanced)"Out Params" are a more controlled version of shell's dynamic scope. They reuse the nameref mechanism.
proc p(s, :myout) { # declare out param with :
setref myout = "prefix-$s" # mutate it to "return" value to caller
}
$(myproc) to retrieve it.__
prefix. This avoids a problem with nameref cycle detection.Shell and bash have grown many mechanisms for "declaring" and mutating variables:
x=foodeclare, local, and readonly-n "nameref" flagExamples:
readonly name=World # no spaces allowed around =
declare foo="Hello $name"
foo=$((42 + a[2]))
declare -n ref=foo # $foo can be written through $ref
These constructs are all discouraged in Oil code.
The "top-level" of the interpreter is used in two situations:
Experienced Oil users should know that keywords like var behave differently
in the top-level scope vs. proc scope. This is due to the tension between
shell's interactive nature and Oil's strictness (and to the dynamic nature of
the source builtin).
For reference, JavaScript's modern let keyword has similar behavior.
Before going into detail on keyword behavior, here are some practical guidelines:
setvar only. This keyword is like Python's
assignment operator: it creates or mutates a variable.
proc.
proc main(@argv).var and const.setvar to mutate local variables, and setglobal
to mutate globals.const declarations. (You can use var,
but it has special rules, explained below.)That's all you need to remember. The following sections explain the rationale for these guidelines.
The lack of static checks affects the recommended usage for both interactive sessions and batch scripts.
setvar onlyAs mentioned, you only need the setvar keyword in an interactive shell:
oil$ setvar x = 42 # create variable 'x'
oil$ setvar x = 43 # mutate it
Details on top-level behavior:
var behaves like setvar: It creates or mutates a variable. In other
words, a var definition can be redefined at the top-level.const can also redefine a var.var can't redefine a const because there's a dynamic check that
disallows mutation (like shell's readonly).const onlyIt's simpler to use only constants at the top level.
const USER = 'bob'
const HOST = 'example.com'
proc p {
ssh $USER@$HOST ls -l
}
This is so you don't have to worry about a var being redefined by a statement
like source mylib.sh. A const can't be redefined because it can't be
mutated.
It may be useful to put mutable globals in a constant dictionary, as it will prevent them from being redefined:
const G = {
mystate = 0
}
proc p {
setglobal G->mystate = 1
}
proc Scope Has Static ChecksProcs are Oil's stricter notion of "shell functions", and they have additional static checks (parse errors):
var or const. A
duplicate declaration is a parse error.const is a parse error.setvar of an undeclared variable is a parse error.Procs are designed to be encapsulated and composable like processes. But Bourne shell functions use a rule called dynamic scope for variable name lookup that breaks encapsulation.
Dynamic scope means that a function can read and mutate the locals of its caller, its caller's caller, and so forth. Example:
g() {
echo "f_var is $f_var" # g can see f's local variables
}
f() {
local f_var=42
g
}
f
Oil code should use proc instead. Inside a proc call, the dynamic_scope
option is implicitly disabled (equivalent to shopt --unset dynamic_scope).
This means that adding the proc keyword to the definition of g changes its
behavior:
proc g() {
echo "f_var is $f_var" # Undefined!
}
This affects all kinds of variable references:
proc p {
echo $foo # look up foo in command mode
var y = foo + 1 # look up foo in expression mode
}
In shell, these language constructs assign to variables using dynamic scope. In Oil, they only mutate the local scope:
x=val
x+=val, a[i]=val, a[i]+=valexport x=val and readonly x=val${x=default}mycmd {x}>out (stores a file descriptor in $x)(( x = 42 + y ))These builtins are also "isolated" inside procs, using local scope:
Oil Builtins:
--assign-statusThe expression to the left of = is called a place. These are basically
Python or JavaScript expressions, except that you add the setvar or
setglobal keyword.
setvar x[1] = 2
setvar d['key'] = 3
setvar d->key = 3 # syntactic sugar for the above
setvar func_returning_list()[3] = 3
setvar x, y = y, x # swap
setvar x.foo, x.bar = foo, bar
constIn Oil, but not OSH, you can omit const when there's only one variable:
const x = 'foo'
x = 'foo' # Same thing. This is NOT a mutation as in C or Java.
To prevent confusion, x=foo (no spaces) is disallowed in Oil. Use the env
command instead:
env PYTHONPATH=. ./foo.py # good
PYTHONPATH=. ./foo.py`. # disallowed because it would be confusing
This section may help experienced shell users understand Oil.
Shell:
g=G # global variable
readonly c=C # global constant
myfunc() {
local x=X # local variable
readonly y=Y # local constant
x=mutated # mutate local
g=mutated # mutate global
newglobal=G # create new global
caller_var=mutated # dynamic scope (Oil doesn't have this)
}
Oil:
var g = 'G' # global variable (discouraged)
const c = 'C' # global constant
proc myproc {
var x = 'L' # local variable
const y = 'Y' # local constant
setvar x = 'mutated' # mutate local
setglobal g = 'mutated' # mutate global
setvar newglobal = 'G' # create new global
# There's no dynamic scope, but you can use
# "out params" with setref.
}
setvar in
interactive shells, and only const in the global scope of programs.var at the top level was partly inspired by this
paper. It's consistent with bash's declare, and similar to JavaScript's
let.