Why Sponsor Oils? | source | all docs for version 0.23.0 | all versions | oilshell.org
Here are some common questions about YSH. Many of the answers boil down to the fact that YSH is a smooth upgrade from bash.
Old and new constructs exist side-by-side. New constructs have fewer "gotchas".
myvar
, $myvar
, and "$myvar"
?YSH is more like Python/JavaScript rather than PHP/Perl, so it doesn't use the
$
sigil as much.
Never use $
on the left-hand side:
var mystr = "foo" # not var $mystr
Use $
to substitute vars into commands:
echo $mystr
echo $mystr/subdir # no quotes in commands
or quoted strings:
echo "$mystr/subdir"
var x = "$mystr/subdir"
Rarely use $
on the right-hand side:
var x = mystr # preferred
var x = $mystr # ILLEGAL -- use remove $
var x = ${mystr:-} # occasionally useful
var x = $? # allowed
See Command vs. Expression Mode for more details.
~/src
or ~bob/git
in a YSH assignment?This should cover 80% of cases:
var path = "$HOME/src" # equivalent to ~/src
The old shell style will cover the remaining cases:
declare path=~/src
readonly other=~bob/git
This is only in issue in expressions. The traditional shell idioms work in command mode:
echo ~/src ~bob/git
# => /home/alice/src /home/bob/git
The underlying design issue is that the YSH expression ~bob
looks like a
unary operator and a variable, not some kind of string substitution.
Also, quoted "~"
is a literal tilde, and shells disagree on what ~""
means.
The rules are subtle, so we avoid inventing new ones.
echo -e
or echo -n
?To echo special characters denoted by backslash escapes, use a
statically-parsed string literal, not echo -e
:
echo u'tab \t newline \n' # YES: J8 style string is recommended in YSH
echo $'tab \t newline \n' # bash-style string is also accepted
These styles don't work in YSH:
echo -e "tab \\t newline \\n" # NO: -e is printed literally
echo -e "tab \t newline \n" # Error: Invalid char escape
To omit the trailing newline, use the write
builtin:
write -n -- $prefix # YES
write --end '' -- $prefix # synonym
echo -n $prefix # NO: -n is printed literally
-e
and -n
Removed?The idioms with u''
and write
are more powerful and consistent.
Moreover, shell's echo
is the only builtin that doesn't accept --
to stop
flag processing.
That is, echo "$flag"
always has a few bugs: when $flag
is -e
, -n
,
-en
, or -ne
. There's no way to fix this bug in POSIX shell.
So portable shell scripts use:
printf '%s\n' "$x" # print $x "unmolested" in POSIX shell
We could have chosen to respect echo -- $x
, but YSH already has:
write -- $x # print $x "unmolested" in YSH
That means YSH has:
echo $x # an even shorter way
So echo
is technically superfluous in YSH, but it's also short, familiar, and
correct.
YSH isn't intended to be compatible with POSIX shell; only OSH is.
$myvar
and \n
?In YSH, either use $[ \n ]
inside a double-quoted string:
$ echo "$myvar $[ \n ] two" # expression sub wraps \n
value_of_myvar
two
Or use the concatenation operator ++
with two styles of string literal:
echo $[u'newline \n' ++ " $year/$month/$day"]
This POSIX shell behavior is probably not what you want:
$ echo "\n"
\n # not a newline!
echo
invocations I need to change when using YSH?A search like this can statically find most usages:
$ egrep -n 'echo (-e|-n|-en|-ne)' *.sh
test/syscall.sh:58: echo -n hi
test/syscall.sh:76: echo -e '\t'
$(dirname $x)
and $[len(x)]
?Superficially, both of these syntaxes take an argument x
and return a
string. But they are different:
$(dirname $x)
is a shell command substitution that returns a string, and
starts another process.$[len(x)]
is an expression sub containing a function call expression.
len(x)
evaluates to an integer, and $[len(x)]
converts it to
a string.${array[r'\']}
?This boils down to the difference between OSH and YSH, and not being able to
mix the two. Though they look similar, ${array[i]}
syntax (with braces) is
fundamentally different than $[array[i]]
syntax (with brackets).
${array[i]}
.
${array[i++]}
or
${assoc["$key"]}
.r'\'
.$[array[i]]
is preferred.
$[array[i + 1]
or $[mydict[key]]
.r'\'
is a valid key, e.g. $[mydict[r'\']]
.Of course, YSH style is preferred when compatibility isn't an issue.
No:
echo ${array[r'\']}
Yes:
echo $[array[r'\']]
A similar issue exists with arithmetic.
Old:
echo $((1 + 2)) # shell arithmetic
New:
echo $[1 + 2] # YSH expression
if (myvar)
and if test -f
?You can use the --true
and --false
flags to the YSH test
builtin:
if test --true $[myvar] && test --file x {
echo ok
}
They test if their argument is literally the string "true"
or "false"
.
This works because the boolean true
stringifies to "true"
, and likewise
with false
.
p
in myproc (&p) | grep foo
?In a pipeline, most components are forked. This means that myproc (&p)
runs in a different process from the main shell.
The main shell can't see the memory of a subshell.
In general, you have to restructure your code to avoid this. You could use a proc with multiple outputs:
myproc (&p, &grepped_output)
Or you could use a function:
var out1, out2 = myfunc(io)
The Unix Shell Process Model - When Are Processes Created? may help.
This issue is similar to the shopt -s lastpipe
issue:
$ bash -c 'echo hi | read x; echo x=$x'
x=
$ zsh -c 'echo hi | read x; echo x=$x'
x=hi
In bash, read
runs in a subshell, but in zsh
and OSH, it runs in the main
shell.