YSH FAQ

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".

Table of Contents
What's the difference myvar, $myvar, and "$myvar" ?
How do I write ~/src or ~bob/git in a YSH assignment?
How do I write the equivalent of echo -e or echo -n?
Why Were -e and -n Removed?
How do I write a string literal with both $myvar and \n?
How do I find all the echo invocations I need to change when using YSH?
What's the difference between $(dirname $x) and $[len(x)] ?
Why doesn't a raw string work here: ${array[r'\']} ?
How do I combine conditional commands and expressions: if (myvar) and if test -f?
Why do I lose the value of p in myproc (&p) | grep foo?
Related

What's the difference 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.

How do I write ~/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.

How do I write the equivalent of 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

Why Were -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.

How do I write a string literal with both $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!

How do I find all the 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'

What's the difference between $(dirname $x) and $[len(x)] ?

Superficially, both of these syntaxes take an argument x and return a string. But they are different:

Why doesn't a raw string work here: ${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).

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

How do I combine conditional commands and expressions: 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.

Why do I lose the value of 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.

Related

Generated on Wed, 30 Oct 2024 20:03:19 +0000