| 1 | ---
 | 
| 2 | default_highlighter: oils-sh
 | 
| 3 | ---
 | 
| 4 | 
 | 
| 5 | YSH FAQ
 | 
| 6 | =======
 | 
| 7 | 
 | 
| 8 | Here are some common questions about [YSH]($xref).  Many of the answers boil
 | 
| 9 | down to the fact that YSH is a **smooth upgrade** from [bash]($xref).
 | 
| 10 | 
 | 
| 11 | Old and new constructs exist side-by-side.  New constructs have fewer
 | 
| 12 | "gotchas".
 | 
| 13 | 
 | 
| 14 | <!-- cmark.py expands this -->
 | 
| 15 | <div id="toc">
 | 
| 16 | </div>
 | 
| 17 | 
 | 
| 18 | ## What's the difference `myvar`, `$myvar`, and `"$myvar"` ?
 | 
| 19 | 
 | 
| 20 | YSH is more like Python/JavaScript rather than PHP/Perl, so it doesn't use the
 | 
| 21 | `$` sigil as much.
 | 
| 22 | 
 | 
| 23 | Never use `$` on the left-hand side:
 | 
| 24 | 
 | 
| 25 |     var mystr = "foo"   # not var $mystr
 | 
| 26 | 
 | 
| 27 | Use `$` to **substitute** vars into commands:
 | 
| 28 | 
 | 
| 29 |     echo $mystr
 | 
| 30 |     echo $mystr/subdir  # no quotes in commands
 | 
| 31 | 
 | 
| 32 | or quoted strings:
 | 
| 33 | 
 | 
| 34 |     echo "$mystr/subdir"
 | 
| 35 |     var x = "$mystr/subdir"
 | 
| 36 | 
 | 
| 37 | Rarely use `$` on the right-hand side:
 | 
| 38 | 
 | 
| 39 |     var x = mystr       # preferred
 | 
| 40 |     var x = $mystr      # ILLEGAL -- use remove $
 | 
| 41 |     var x = ${mystr:-}  # occasionally useful
 | 
| 42 | 
 | 
| 43 |     var x = $?          # allowed
 | 
| 44 | 
 | 
| 45 | See [Command vs. Expression Mode](command-vs-expression-mode.html) for more
 | 
| 46 | details.
 | 
| 47 | 
 | 
| 48 | ## How do I write `~/src` or `~bob/git` in a YSH assignment?
 | 
| 49 | 
 | 
| 50 | This should cover 80% of cases:
 | 
| 51 | 
 | 
| 52 |     var path = "$HOME/src"  # equivalent to ~/src
 | 
| 53 | 
 | 
| 54 | The old shell style will cover the remaining cases:
 | 
| 55 | 
 | 
| 56 |     declare path=~/src
 | 
| 57 |     readonly other=~bob/git
 | 
| 58 | 
 | 
| 59 | ---
 | 
| 60 | 
 | 
| 61 | This is only in issue in *expressions*.  The traditional shell idioms work in
 | 
| 62 | *command* mode:
 | 
| 63 | 
 | 
| 64 |     echo ~/src ~bob/git
 | 
| 65 |     # => /home/alice/src /home/bob/git
 | 
| 66 | 
 | 
| 67 | The underlying design issue is that the YSH expression `~bob` looks like a
 | 
| 68 | unary operator and a variable, not some kind of string substitution.
 | 
| 69 | 
 | 
| 70 | Also, quoted `"~"` is a literal tilde, and shells disagree on what `~""` means.
 | 
| 71 | The rules are subtle, so we avoid inventing new ones.
 | 
| 72 | 
 | 
| 73 | ## How do I write the equivalent of `echo -e` or `echo -n`?
 | 
| 74 | 
 | 
| 75 | To echo special characters denoted by backslash escapes, use a
 | 
| 76 | statically-parsed string literal, not `echo -e`:
 | 
| 77 | 
 | 
| 78 |     echo u'tab \t newline \n'       # YES: J8 style string is recommended in YSH
 | 
| 79 |     echo $'tab \t newline \n'       #      bash-style string is also accepted
 | 
| 80 | 
 | 
| 81 | These styles don't work in YSH:
 | 
| 82 | 
 | 
| 83 |     echo -e "tab \\t newline \\n"   # NO: -e is printed literally
 | 
| 84 |     echo -e "tab \t newline \n"     #     Error: Invalid char escape
 | 
| 85 | 
 | 
| 86 | To mix backslash escapes and var substitution, use the concatenation operator
 | 
| 87 | `++`:
 | 
| 88 | 
 | 
| 89 |     echo $[u'tab \t' ++ " $year/$month/$day"]
 | 
| 90 | 
 | 
| 91 | To omit the trailing newline, use the `write` builtin:
 | 
| 92 | 
 | 
| 93 |     write -n       -- $prefix       # YES
 | 
| 94 |     write --end '' -- $prefix       # synonym
 | 
| 95 | 
 | 
| 96 |     echo -n $prefix                 # NO: -n is printed literally
 | 
| 97 | 
 | 
| 98 | ### Why Were `-e` and `-n` Removed?
 | 
| 99 | 
 | 
| 100 | The idioms with `u''` and `write` are more powerful and consistent.
 | 
| 101 | 
 | 
| 102 | Moreover, shell's `echo` is the *only* builtin that doesn't accept `--` to stop
 | 
| 103 | flag processing.
 | 
| 104 | 
 | 
| 105 | That is, `echo "$flag"` always has a few bugs: when `$flag` is `-e`, `-n`,
 | 
| 106 | `-en`, or `-ne`. There's **no** way to fix this bug in POSIX shell.
 | 
| 107 | 
 | 
| 108 | So portable shell scripts use:
 | 
| 109 | 
 | 
| 110 |     printf '%s\n' "$x"  # print $x "unmolested" in POSIX shell
 | 
| 111 | 
 | 
| 112 | We could have chosen to respect `echo -- $x`, but YSH already has:
 | 
| 113 | 
 | 
| 114 |     write -- $x         # print $x "unmolested" in YSH
 | 
| 115 | 
 | 
| 116 | That means YSH has:
 | 
| 117 | 
 | 
| 118 |     echo $x             # an even shorter way
 | 
| 119 | 
 | 
| 120 | So `echo` is technically superfluous in YSH, but it's also short, familiar, and
 | 
| 121 | correct.
 | 
| 122 | 
 | 
| 123 | YSH isn't intended to be compatible with POSIX shell; only OSH is.
 | 
| 124 | 
 | 
| 125 | ### How do I find all the `echo` invocations I need to change when using YSH?
 | 
| 126 | 
 | 
| 127 | A search like this can statically find most usages:
 | 
| 128 | 
 | 
| 129 |     $ egrep -n 'echo (-e|-n|-en|-ne)' *.sh
 | 
| 130 |     test/syscall.sh:58: echo -n hi
 | 
| 131 |     test/syscall.sh:76: echo -e '\t'
 | 
| 132 | 
 | 
| 133 | ## What's the difference between `$(dirname $x)` and `$[len(x)]` ?
 | 
| 134 | 
 | 
| 135 | Superficially, both of these syntaxes take an argument `x` and return a
 | 
| 136 | string.  But they are different:
 | 
| 137 | 
 | 
| 138 | - `$(dirname $x)` is a shell command substitution that returns a string, and
 | 
| 139 |   **starts another process**.
 | 
| 140 | - `$[len(x)]` is an expression sub containing a function call expression.
 | 
| 141 |   - It doesn't need to start a process.
 | 
| 142 |   - Note that `len(x)` evaluates to an integer, and `$[len(x)]` converts it to
 | 
| 143 |     a string.
 | 
| 144 | 
 | 
| 145 | <!--
 | 
| 146 | (Note: builtin subs like `${.myproc $x}` are meant to eliminate process
 | 
| 147 | overhead, but they're not yet implemented.)
 | 
| 148 | -->
 | 
| 149 | 
 | 
| 150 | ## Why doesn't a raw string work here: `${array[r'\']}` ?
 | 
| 151 | 
 | 
| 152 | This boils down to the difference between OSH and YSH, and not being able to
 | 
| 153 | mix the two.  Though they look similar, `${array[i]}` syntax (with braces) is
 | 
| 154 | fundamentally different than `$[array[i]]` syntax (with brackets).
 | 
| 155 | 
 | 
| 156 | - OSH supports `${array[i]}`.
 | 
| 157 |   - The index is legacy/deprecated shell arithmetic like `${array[i++]}` or
 | 
| 158 |     `${assoc["$key"]}`.
 | 
| 159 |   - The index **cannot** be a raw string like `r'\'`.
 | 
| 160 | - YSH supports both, but [expression substitution][expr-sub] syntax
 | 
| 161 |   `$[array[i]]` is preferred.
 | 
| 162 |   - It accepts YSH expressions like `$[array[i + 1]` or `$[mydict[key]]`.
 | 
| 163 |   - A raw string like `r'\'` is a valid key, e.g.  `$[mydict[r'\']]`.
 | 
| 164 | 
 | 
| 165 | [expr-sub]: ref/chap-expr-lang.html#expr-sub
 | 
| 166 | 
 | 
| 167 | Of course, YSH style is preferred when compatibility isn't an issue.
 | 
| 168 | 
 | 
| 169 | No:
 | 
| 170 | 
 | 
| 171 |     echo ${array[r'\']}
 | 
| 172 | 
 | 
| 173 | Yes:
 | 
| 174 | 
 | 
| 175 |     echo $[array[r'\']]
 | 
| 176 | 
 | 
| 177 | A similar issue exists with arithmetic.
 | 
| 178 | 
 | 
| 179 | Old:
 | 
| 180 | 
 | 
| 181 |     echo $((1 + 2))   # shell arithmetic
 | 
| 182 | 
 | 
| 183 | New:
 | 
| 184 | 
 | 
| 185 |     echo $[1 + 2]     # YSH expression
 | 
| 186 | 
 | 
| 187 | <!--
 | 
| 188 | 
 | 
| 189 | ## Why doesn't the ternary operator work here: `${array[0 if cond else 5]}`?
 | 
| 190 | 
 | 
| 191 | The issue is the same as above.  YSH expression are allowed within `$[]` but
 | 
| 192 | not `${}`.
 | 
| 193 | 
 | 
| 194 | -->
 | 
| 195 | 
 | 
| 196 | ## Related
 | 
| 197 | 
 | 
| 198 | - [Oil Language FAQ]($wiki) on the wiki has more answers.  They may be migrated
 | 
| 199 |   here at some point.
 | 
| 200 | 
 |