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