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

216 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](ref/chap-option.html#ysh:all) 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](ref/chap-option.html#ysh:all) option enforces that
68`test` receives 3 or fewer arguments.
69
70### Prefer Shell Functions to Aliases
71
72Functions subsume all the common uses of alias, and can be parsed statically.
73
74No:
75
76 alias ll='ls -l'
77
78Yes:
79
80 ll() { # Shell Style
81 ls -l "$@"
82 }
83
84 proc ll { # YSH Style
85 ls -l @ARGV
86 }
87
88If you're wrapping an external command with a function of the same, use the
89[command](ref/chap-builtin-cmd.html#command) builtin:
90
91 proc ls {
92 command ls --color @ARGV
93 }
94
95### Prefer `$'\n'` to `echo -e`
96
97No:
98
99 echo -e '\n' # arg to -e is dynamically parsed
100
101Yes:
102
103 echo $'\n' # statically parsed
104
105## How to Fix Code That `strict_errexit` Disallows
106
107The `strict_errexit` feature warns you when you would **lose errors** in shell
108code.
109
110### The `local d=$(date %x)` Pitfall
111
112No:
113
114 local d=$(date %x) # ignores failure
115
116Yes:
117
118 local d
119 d=$(date %x) # fails properly
120
121Better YSH style:
122
123 var d = $(date %x) # fails properly
124
125### Variations With `readonly` and `export`
126
127In these cases, the builtin comes after the assignment.
128
129No:
130
131 readonly d1=$(date %x)
132 export d2=$(date %x)
133
134Yes:
135
136 d1=$(date %x)
137 readonly d1
138
139 d2=$(date %x)
140 export d2
141
142
143### The `if myfunc` Pitfall
144
145No:
146
147 if myfunc; then
148 echo 'Success'
149 fi
150
151Shell workaround when the *$0 Dispatch Pattern* is used:
152
153 myfunc() {
154 echo hi
155 }
156
157 mycaller() {
158 if $0 myfunc; then # $0 starts this script as a new process
159 echo 'Success'
160 fi
161 }
162
163 "$@" # invoked like myscript.sh mycaller arg1 arg2 ...
164
165
166Better YSH Style:
167
168 try myfunc
169 if (_status === 0)
170 echo 'Success'
171 }
172
173
174## Remove Dynamic Parsing
175
176### Replacing `declare -i`, `local -i`, ...
177
178The `-i` flag on assignment builtins doesn't add any functionality to bash &mdash;
179it's merely a different and confusing style.
180
181OSH doesn't support it; instead it has *true integers*.
182
183For example, don't rely on "punning" of the `+=` operator; use `$(( ))`
184instead.
185
186No:
187
188 declare -i x=3
189 x+=1 # Now it's '4' because += will do integer arithmetic
190
191Yes (shell style):
192
193 x=3
194 x=$(( x + 1 )) # No -i flag needed
195
196Yes (YSH style):
197
198 var x = 3
199 setvar x += 1
200
201Likewise, don't rely on dynamic parsing of arithmetic.
202
203No:
204
205 declare -i x
206 x='1 + 2' # Now it's the string '3'
207
208Yes (shell style):
209
210 x=$(( 1 + 2 ))
211
212Yes (YSH style):
213
214 var x = 1 + 2
215
216