OILS / doc / shell-idioms.md View on Github | oilshell.org

217 lines, 128 significant
1---
2default_highlighter: oils-sh
3---
4
5Shell Language Idioms
6=====================
7
8These are like the [YSH vs. Shell Idioms](idioms.html), but the advice also
9applies to other Unix shells.
10
11<div id="toc">
12</div>
13
14## Style
15
16### Use Only `"$@"`
17
18There's no reason to use anything but `"$@"`. All the other forms like `$*`
19can be disallowed, because if you want to join to a string, you can write:
20
21 joined_str="$@"
22
23The same advice applies to arrays. You can always use `"${myarray[@]}"`; you
24never need to use `${myarray[*]}` or any other form.
25
26Related: [Thirteen Incorrect Ways and Two Awkward Ways to Use
27Arrays](http://www.oilshell.org/blog/2016/11/06.html)
28
29### Prefer `test` to `[`
30
31Idiomatic OSH code doesn't use "puns".
32
33No:
34
35 [ -d /tmp ]
36
37Yes:
38
39 test -d /tmp
40
41The [simple_test_builtin]($oil-help) option enforces this.
42
43## Use Statically Parsed Language Constructs
44
45Static parsing is one of the [syntactic concepts](syntactic-concepts.html). It
46leads to better error messages, earlier error messages, and lets tools
47understand your code.
48
49### `test` Should Only Have 2 or 3 Arguments
50
51In POSIX, the `test` builtin has a lot of unnecessary flexibility, which leads
52to bugs.
53
54See [Problems With the test Builtin: What Does -a
55Mean?](//www.oilshell.org/blog/2017/08/31.html)
56
57No:
58
59 test ! -d /tmp
60 test -d /tmp -a -d /tmp/foo
61
62Yes:
63
64 ! test -d /tmp
65 test -d /tmp && test -d /tmp/foo
66
67The [simple_test_builtin]($oil-help) option enforces that `test` receives 3 or
68fewer arguments.
69
70
71### Prefer Shell Functions to Aliases
72
73Functions subsume all the common uses of alias, and can be parsed statically.
74
75No:
76
77 alias ll='ls -l'
78
79Yes:
80
81 ll() { # Shell Style
82 ls -l "$@"
83 }
84
85 proc ll { # YSH Style
86 ls -l @ARGV
87 }
88
89If you're wrapping an external command with a function of the same, use the
90[command]($osh-help) builtin:
91
92 proc ls {
93 command ls --color @ARGV
94 }
95
96### Prefer `$'\n'` to `echo -e`
97
98No:
99
100 echo -e '\n' # arg to -e is dynamically parsed
101
102Yes:
103
104 echo $'\n' # statically parsed
105
106## How to Fix Code That `strict_errexit` Disallows
107
108The `strict_errexit` feature warns you when you would **lose errors** in shell
109code.
110
111### The `local d=$(date %x)` Pitfall
112
113No:
114
115 local d=$(date %x) # ignores failure
116
117Yes:
118
119 local d
120 d=$(date %x) # fails properly
121
122Better YSH style:
123
124 var d = $(date %x) # fails properly
125
126### Variations With `readonly` and `export`
127
128In these cases, the builtin comes after the assignment.
129
130No:
131
132 readonly d1=$(date %x)
133 export d2=$(date %x)
134
135Yes:
136
137 d1=$(date %x)
138 readonly d1
139
140 d2=$(date %x)
141 export d2
142
143
144### The `if myfunc` Pitfall
145
146No:
147
148 if myfunc; then
149 echo 'Success'
150 fi
151
152Shell workaround when the *$0 Dispatch Pattern* is used:
153
154 myfunc() {
155 echo hi
156 }
157
158 mycaller() {
159 if $0 myfunc; then # $0 starts this script as a new process
160 echo 'Success'
161 fi
162 }
163
164 "$@" # invoked like myscript.sh mycaller arg1 arg2 ...
165
166
167Better YSH Style:
168
169 try myfunc
170 if (_status === 0)
171 echo 'Success'
172 }
173
174
175## Remove Dynamic Parsing
176
177### Replacing `declare -i`, `local -i`, ...
178
179The `-i` flag on assignment builtins doesn't add any functionality to bash &mdash;
180it's merely a different and confusing style.
181
182OSH doesn't support it; instead it has *true integers*.
183
184For example, don't rely on "punning" of the `+=` operator; use `$(( ))`
185instead.
186
187No:
188
189 declare -i x=3
190 x+=1 # Now it's '4' because += will do integer arithmetic
191
192Yes (shell style):
193
194 x=3
195 x=$(( x + 1 )) # No -i flag needed
196
197Yes (YSH style):
198
199 var x = 3
200 setvar x += 1
201
202Likewise, don't rely on dynamic parsing of arithmetic.
203
204No:
205
206 declare -i x
207 x='1 + 2' # Now it's the string '3'
208
209Yes (shell style):
210
211 x=$(( 1 + 2 ))
212
213Yes (YSH style):
214
215 var x = 1 + 2
216
217