1 #!/usr/bin/env bash
2 #
3 # Usage:
4 # ./errexit.test.sh <function name>
5
6 #### errexit aborts early
7 set -o errexit
8 false
9 echo done
10 ## stdout-json: ""
11 ## status: 1
12
13 #### errexit for nonexistent command
14 set -o errexit
15 nonexistent__ZZ
16 echo done
17 ## stdout-json: ""
18 ## status: 127
19
20 #### errexit aborts early on pipeline
21 set -o errexit
22 echo hi | grep nonexistent
23 echo two
24 ## stdout-json: ""
25 ## status: 1
26
27 #### errexit with { }
28 # This aborts because it's not part of an if statement.
29 set -o errexit
30 { echo one; false; echo two; }
31 ## stdout: one
32 ## status: 1
33
34 #### errexit with if and { }
35 set -o errexit
36 if { echo one; false; echo two; }; then
37 echo three
38 fi
39 echo four
40 ## status: 0
41 ## STDOUT:
42 one
43 two
44 three
45 four
46 ## END
47
48 #### errexit with ||
49 set -o errexit
50 echo hi | grep nonexistent || echo ok
51 ## stdout: ok
52 ## status: 0
53
54 #### errexit with &&
55 set -o errexit
56 echo ok && echo hi | grep nonexistent
57 ## stdout: ok
58 ## status: 1
59
60 #### errexit test && -- from gen-module-init
61 set -o errexit
62 test "$mod" = readline && echo "#endif"
63 echo status=$?
64 ## stdout: status=1
65
66 #### errexit test && and fail
67 set -o errexit
68 test -n X && false
69 echo status=$?
70 ## stdout-json: ""
71 ## status: 1
72
73 #### errexit and loop
74 set -o errexit
75 for x in 1 2 3; do
76 test $x = 2 && echo "hi $x"
77 done
78 ## stdout: hi 2
79 ## status: 1
80
81 #### errexit and brace group { }
82 set -o errexit
83 { test no = yes && echo hi; }
84 echo status=$?
85 ## stdout: status=1
86
87 #### errexit and time { }
88 set -o errexit
89 time false
90 echo status=$?
91 ## status: 1
92
93 #### errexit with !
94 set -o errexit
95 echo one
96 ! true
97 echo two
98 ! false
99 echo three
100 ## STDOUT:
101 one
102 two
103 three
104 ## END
105
106 #### errexit with ! and ;
107 # AST has extra Sentence nodes; there was a REGRESSION here.
108 set -o errexit; echo one; ! true; echo two; ! false; echo three
109 ## STDOUT:
110 one
111 two
112 three
113 ## END
114
115 #### errexit with while/until
116 set -o errexit
117 while false; do
118 echo ok
119 done
120 until false; do
121 echo ok # do this once then exit loop
122 break
123 done
124 ## stdout: ok
125 ## status: 0
126
127 #### errexit with (( ))
128 # from http://mywiki.wooledge.org/BashFAQ/105, this changed between verisons.
129 # ash says that 'i++' is not found, but it doesn't exit. I guess this is the
130 # subshell problem?
131 set -o errexit
132 i=0
133 (( i++ ))
134 echo done
135 ## stdout-json: ""
136 ## status: 1
137 ## N-I dash/ash status: 127
138 ## N-I dash/ash stdout-json: ""
139
140 #### errexit with subshell
141 set -o errexit
142 ( echo one; false; echo two; )
143 echo three
144 ## status: 1
145 ## STDOUT:
146 one
147 ## END
148
149 #### set -o errexit while it's being ignored (moot with strict_errexit)
150 set -o errexit
151 # osh aborts early here
152 if { echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4; }; then
153 echo 5;
154 fi
155 echo 6
156 false # this is the one that makes shells fail
157 echo 7
158 ## status: 1
159 ## STDOUT:
160 1
161 2
162 3
163 4
164 5
165 6
166 ## END
167
168 #### set +o errexit while it's being ignored (moot with strict_errexit)
169 set -o errexit
170 if { echo 1; false; echo 2; set +o errexit; echo 3; false; echo 4; }; then
171 echo 5;
172 fi
173 echo 6
174 false # does NOT fail, because we restored it.
175 echo 7
176 ## STDOUT:
177 1
178 2
179 3
180 4
181 5
182 6
183 7
184 ## END
185
186 #### setting errexit in a subshell works but doesn't affect parent shell
187 ( echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4; )
188 echo 5
189 false
190 echo 6
191 ## STDOUT:
192 1
193 2
194 3
195 5
196 6
197 ## END
198
199 #### set errexit while it's ignored in a subshell (moot with strict_errexit)
200 set -o errexit
201 if ( echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4 ); then
202 echo 5;
203 fi
204 echo 6 # This is executed because the subshell just returns false
205 false
206 echo 7
207 ## status: 1
208 ## STDOUT:
209 1
210 2
211 3
212 4
213 5
214 6
215 ## END
216
217 #### shopt -s strict:all || true while errexit is on
218 set -o errexit
219 shopt -s strict:all || true
220 echo one
221 false # fail
222 echo two
223 ## status: 1
224 ## STDOUT:
225 one
226 ## END
227
228 #### errexit double guard
229 # OSH bug fix. ErrExit needs a counter, not a boolean.
230 set -o errexit
231 if { ! false; false; true; } then
232 echo true
233 fi
234 false
235 echo done
236 ## status: 1
237 ## STDOUT:
238 true
239 ## END
240
241 #### background processes respect errexit
242 set -o errexit
243 { echo one; false; echo two; exit 42; } &
244 wait $!
245 ## status: 1
246 ## STDOUT:
247 one
248 ## END
249
250 #### pipeline process respects errexit
251 set -o errexit
252 # It is respected here.
253 { echo one; false; echo two; } | cat
254
255 # Also respected here.
256 { echo three; echo four; } | while read line; do
257 echo "[$line]"
258 false
259 done
260 echo four
261 ## status: 1
262 ## STDOUT:
263 one
264 [three]
265 ## END
266
267 #### compound command
268 # case from
269 # https://lists.gnu.org/archive/html/bug-bash/2020-05/msg00066.html
270
271 set -o errexit
272
273 { cat ; } < not_exist.txt
274
275 echo status=$?
276 echo 'should not get here'
277 ## status: 1
278 ## stdout-json: ""
279 ## BUG dash/bash/ash status: 0
280 ## BUG bash/ash STDOUT:
281 status=1
282 should not get here
283 ## END
284 ## BUG dash STDOUT:
285 status=2
286 should not get here
287 ## END
288
289 #### while loop
290 # case from
291 # https://lists.gnu.org/archive/html/bug-bash/2020-05/msg00066.html
292
293 set -o errexit
294
295 while read line; do
296 echo $line
297 done < not_exist.txt
298
299 echo status=$?
300 echo 'should not get here'
301 ## status: 1
302 ## stdout-json: ""
303 ## BUG dash/bash/ash status: 0
304 ## BUG bash/ash STDOUT:
305 status=1
306 should not get here
307 ## END
308 ## BUG dash STDOUT:
309 status=2
310 should not get here
311 ## END
312
313 #### set -e enabled in function (regression)
314 set +e
315 foo() {
316 set -e
317 false
318 echo "should be executed"
319 }
320 foo && true
321 echo "should be executed"
322 ## STDOUT:
323 should be executed
324 should be executed
325 ## END