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

200 lines, 124 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 mix backslash escapes and var substitution, use the concatenation operator
87`++`:
88
89 echo $[u'tab \t' ++ " $year/$month/$day"]
90
91To 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
100The idioms with `u''` and `write` are more powerful and consistent.
101
102Moreover, shell's `echo` is the *only* builtin that doesn't accept `--` to stop
103flag processing.
104
105That 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
108So portable shell scripts use:
109
110 printf '%s\n' "$x" # print $x "unmolested" in POSIX shell
111
112We could have chosen to respect `echo -- $x`, but YSH already has:
113
114 write -- $x # print $x "unmolested" in YSH
115
116That means YSH has:
117
118 echo $x # an even shorter way
119
120So `echo` is technically superfluous in YSH, but it's also short, familiar, and
121correct.
122
123YSH 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
127A 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
135Superficially, both of these syntaxes take an argument `x` and return a
136string. 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
147overhead, but they're not yet implemented.)
148-->
149
150## Why doesn't a raw string work here: `${array[r'\']}` ?
151
152This boils down to the difference between OSH and YSH, and not being able to
153mix the two. Though they look similar, `${array[i]}` syntax (with braces) is
154fundamentally 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
167Of course, YSH style is preferred when compatibility isn't an issue.
168
169No:
170
171 echo ${array[r'\']}
172
173Yes:
174
175 echo $[array[r'\']]
176
177A similar issue exists with arithmetic.
178
179Old:
180
181 echo $((1 + 2)) # shell arithmetic
182
183New:
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
191The issue is the same as above. YSH expression are allowed within `$[]` but
192not `${}`.
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