OILS / doc / ysh-faq.md View on Github | oilshell.org

262 lines, 160 significant
1---
2default_highlighter: oils-sh
3---
4
5YSH FAQ
6=======
7
8Here are some common questions about [YSH]($xref). Many of the answers boil
9down to the fact that YSH is a **smooth upgrade** from [bash]($xref).
10
11Old 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
20YSH is more like Python/JavaScript rather than PHP/Perl, so it doesn't use the
21`$` sigil as much.
22
23Never use `$` on the left-hand side:
24
25 var mystr = "foo" # not var $mystr
26
27Use `$` to **substitute** vars into commands:
28
29 echo $mystr
30 echo $mystr/subdir # no quotes in commands
31
32or quoted strings:
33
34 echo "$mystr/subdir"
35 var x = "$mystr/subdir"
36
37Rarely 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
45See [Command vs. Expression Mode](command-vs-expression-mode.html) for more
46details.
47
48## How do I write `~/src` or `~bob/git` in a YSH assignment?
49
50This should cover 80% of cases:
51
52 var path = "$HOME/src" # equivalent to ~/src
53
54The old shell style will cover the remaining cases:
55
56 declare path=~/src
57 readonly other=~bob/git
58
59---
60
61This 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
67The underlying design issue is that the YSH expression `~bob` looks like a
68unary operator and a variable, not some kind of string substitution.
69
70Also, quoted `"~"` is a literal tilde, and shells disagree on what `~""` means.
71The rules are subtle, so we avoid inventing new ones.
72
73## How do I write the equivalent of `echo -e` or `echo -n`?
74
75To echo special characters denoted by backslash escapes, use a
76statically-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
81These 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
86To omit the trailing newline, use the `write` builtin:
87
88 write -n -- $prefix # YES
89 write --end '' -- $prefix # synonym
90
91 echo -n $prefix # NO: -n is printed literally
92
93### Why Were `-e` and `-n` Removed?
94
95The idioms with `u''` and `write` are more powerful and consistent.
96
97Moreover, shell's `echo` is the *only* builtin that doesn't accept `--` to stop
98flag processing.
99
100That is, `echo "$flag"` always has a few bugs: when `$flag` is `-e`, `-n`,
101`-en`, or `-ne`. There's **no** way to fix this bug in POSIX shell.
102
103So portable shell scripts use:
104
105 printf '%s\n' "$x" # print $x "unmolested" in POSIX shell
106
107We could have chosen to respect `echo -- $x`, but YSH already has:
108
109 write -- $x # print $x "unmolested" in YSH
110
111That means YSH has:
112
113 echo $x # an even shorter way
114
115So `echo` is technically superfluous in YSH, but it's also short, familiar, and
116correct.
117
118YSH isn't intended to be compatible with POSIX shell; only OSH is.
119
120### How do I write a string literal with both `$myvar` and `\n`?
121
122In YSH, either use `$[ \n ]` inside a double-quoted string:
123
124 $ echo "$myvar $[ \n ] two" # expression sub wraps \n
125 value_of_myvar
126 two
127
128Or use the concatenation operator `++` with two styles of string literal:
129
130 echo $[u'newline \n' ++ " $year/$month/$day"]
131
132This POSIX shell behavior is probably not what you want:
133
134 $ echo "\n"
135 \n # not a newline!
136
137### How do I find all the `echo` invocations I need to change when using YSH?
138
139A search like this can statically find most usages:
140
141 $ egrep -n 'echo (-e|-n|-en|-ne)' *.sh
142 test/syscall.sh:58: echo -n hi
143 test/syscall.sh:76: echo -e '\t'
144
145## What's the difference between `$(dirname $x)` and `$[len(x)]` ?
146
147Superficially, both of these syntaxes take an argument `x` and return a
148string. But they are different:
149
150- `$(dirname $x)` is a shell command substitution that returns a string, and
151 **starts another process**.
152- `$[len(x)]` is an expression sub containing a function call expression.
153 - It doesn't need to start a process.
154 - Note that `len(x)` evaluates to an integer, and `$[len(x)]` converts it to
155 a string.
156
157<!--
158(Note: builtin subs like `${.myproc $x}` are meant to eliminate process
159overhead, but they're not yet implemented.)
160-->
161
162## Why doesn't a raw string work here: `${array[r'\']}` ?
163
164This boils down to the difference between OSH and YSH, and not being able to
165mix the two. Though they look similar, `${array[i]}` syntax (with braces) is
166fundamentally different than `$[array[i]]` syntax (with brackets).
167
168- OSH supports `${array[i]}`.
169 - The index is legacy/deprecated shell arithmetic like `${array[i++]}` or
170 `${assoc["$key"]}`.
171 - The index **cannot** be a raw string like `r'\'`.
172- YSH supports both, but [expression substitution][expr-sub] syntax
173 `$[array[i]]` is preferred.
174 - It accepts YSH expressions like `$[array[i + 1]` or `$[mydict[key]]`.
175 - A raw string like `r'\'` is a valid key, e.g. `$[mydict[r'\']]`.
176
177[expr-sub]: ref/chap-expr-lang.html#expr-sub
178
179Of course, YSH style is preferred when compatibility isn't an issue.
180
181No:
182
183 echo ${array[r'\']}
184
185Yes:
186
187 echo $[array[r'\']]
188
189A similar issue exists with arithmetic.
190
191Old:
192
193 echo $((1 + 2)) # shell arithmetic
194
195New:
196
197 echo $[1 + 2] # YSH expression
198
199<!--
200
201## Why doesn't the ternary operator work here: `${array[0 if cond else 5]}`?
202
203The issue is the same as above. YSH expression are allowed within `$[]` but
204not `${}`.
205
206-->
207
208## How do I combine conditional commands and expressions: `if (myvar)` and `if test -f`?
209
210You can use the `--true` and `--false` flags to the [YSH test][ysh-test]
211builtin:
212
213 if test --true $[myvar] && test --file x {
214 echo ok
215 }
216
217They test if their argument is literally the string `"true"` or `"false"`.
218
219This works because the boolean `true` *stringifies* to `"true"`, and likewise
220with `false`.
221
222[ysh-test]: ref/chap-builtin-cmd.html#ysh-test
223
224
225## Why do I lose the value of `p` in `myproc (&p) | grep foo`?
226
227In a pipeline, most components are **forked**. This means that `myproc (&p)`
228runs in a different process from the main shell.
229
230The main shell can't see the memory of a subshell.
231
232---
233
234In general, you have to restructure your code to avoid this. You could use a proc with multiple outputs:
235
236 myproc (&p, &grepped_output)
237
238Or you could use a function:
239
240 var out1, out2 = myfunc(io)
241
242---
243
244[The Unix Shell Process Model - When Are Processes
245Created?](process-model.html) may help.
246
247This issue is similar to the `shopt -s lastpipe` issue:
248
249 $ bash -c 'echo hi | read x; echo x=$x'
250 x=
251
252 $ zsh -c 'echo hi | read x; echo x=$x'
253 x=hi
254
255In bash, `read` runs in a subshell, but in `zsh` and OSH, it runs in the main
256shell.
257
258## Related
259
260- [Oil Language FAQ]($wiki) on the wiki has more answers. They may be migrated
261 here at some point.
262