1 #!/usr/bin/env bash
2 #
3 # Test set flags, sh flags.
4
5 ### $- with -c
6 # dash's behavior seems most sensible here?
7 $SH -o nounset -c 'echo $-'
8 # stdout: u
9 # OK bash stdout: huBc
10 # OK mksh stdout: uhc
11 # status: 0
12
13 ### $- with pipefail
14 set -o pipefail -o nounset
15 echo $-
16 # stdout: u
17 # status: 0
18 # OK bash stdout: huB
19 # OK mksh stdout: ush
20 # N-I dash stdout-json: ""
21 # N-I dash status: 2
22
23 ### sh -c
24 $SH -c 'echo hi'
25 # stdout: hi
26 # status: 0
27
28 ### empty -c input
29 # had a bug here
30 $SH -c ''
31 # stdout-json: ""
32 # status: 0
33
34 ### empty stdin
35 # had a bug here
36 echo -n '' | $SH
37 # stdout-json: ""
38 # status: 0
39
40 ### args are passed
41 $SH -c 'argv.py "$@"' dummy a b
42 # stdout: ['a', 'b']
43
44 ### args that look like flags are passed after script
45 script=$TMP/sh1.sh
46 echo 'argv.py "$@"' > $script
47 chmod +x $script
48 $SH $script --help --help -h
49 # stdout: ['--help', '--help', '-h']
50
51 ### args that look like flags are passed after -c
52 $SH -c 'argv.py "$@"' --help --help -h
53 # stdout: ['--help', '-h']
54
55 ### pass short options on command line
56 $SH -e -c 'false; echo status=$?'
57 # stdout-json: ""
58 # status: 1
59
60 ### pass long options on command line
61 $SH -o errexit -c 'false; echo status=$?'
62 # stdout-json: ""
63 # status: 1
64
65 ### can continue after unknown option
66 # dash and mksh make this a fatal error no matter what.
67 set -o errexit
68 set -o STRICT || true # unknown option
69 echo hello
70 # stdout: hello
71 # status: 0
72 # BUG dash/mksh stdout-json: ""
73 # BUG dash status: 2
74 # BUG mksh status: 1
75
76 ### set with both options and argv
77 set -o errexit a b c
78 echo "$@"
79 false
80 echo done
81 # stdout: a b c
82 # status: 1
83
84 ### nounset
85 echo "[$unset]"
86 set -o nounset
87 echo "[$unset]"
88 echo end # never reached
89 # stdout: []
90 # status: 1
91 # OK dash status: 2
92
93 ### -u is nounset
94 echo "[$unset]"
95 set -u
96 echo "[$unset]"
97 echo end # never reached
98 # stdout: []
99 # status: 1
100 # OK dash status: 2
101
102 ### nounset with "$@"
103 set a b c
104 set -u # shouldn't touch argv
105 echo "$@"
106 # stdout: a b c
107
108 ### set -u -- clears argv
109 set a b c
110 set -u -- # shouldn't touch argv
111 echo "$@"
112 # stdout:
113
114 ### set -u -- x y z
115 set a b c
116 set -u -- x y z
117 echo "$@"
118 # stdout: x y z
119
120 ### reset option with long flag
121 set -o errexit
122 set +o errexit
123 echo "[$unset]"
124 # stdout: []
125 # status: 0
126
127 ### reset option with short flag
128 set -u
129 set +u
130 echo "[$unset]"
131 # stdout: []
132 # status: 0
133
134 ### set -eu (flag parsing)
135 set -eu
136 echo "[$unset]"
137 echo status=$?
138 # stdout-json: ""
139 # status: 1
140 # OK dash status: 2
141
142 ### -n for no execution (useful with --ast-output)
143 # NOTE: set +n doesn't work because nothing is executed!
144 echo 1
145 set -n
146 echo 2
147 set +n
148 echo 3
149 # stdout-json: "1\n"
150 # status: 0
151
152 ### xtrace
153 echo 1
154 set -o xtrace
155 echo 2
156 # stdout-json: "1\n2\n"
157 # stderr: + echo 2
158
159 ### errexit aborts early
160 set -o errexit
161 false
162 echo done
163 # stdout-json: ""
164 # status: 1
165
166 ### errexit for nonexistent command
167 set -o errexit
168 nonexistent__ZZ
169 echo done
170 # stdout-json: ""
171 # status: 127
172
173 ### errexit aborts early on pipeline
174 set -o errexit
175 echo hi | grep nonexistent
176 echo two
177 # stdout-json: ""
178 # status: 1
179
180 ### errexit with { }
181 # This aborts because it's not part of an if statement.
182 set -o errexit
183 { echo one; false; echo two; }
184 # stdout: one
185 # status: 1
186
187 ### errexit with if and { }
188 set -o errexit
189 if { echo one; false; echo two; }; then
190 echo three
191 fi
192 echo four
193 # stdout-json: "one\ntwo\nthree\nfour\n"
194 # status: 0
195
196 ### errexit with ||
197 set -o errexit
198 echo hi | grep nonexistent || echo ok
199 # stdout: ok
200 # status: 0
201
202 ### errexit with &&
203 set -o errexit
204 echo ok && echo hi | grep nonexistent
205 # stdout: ok
206 # status: 1
207
208 ### errexit test && -- from gen-module-init
209 set -o errexit
210 test "$mod" = readline && echo "#endif"
211 echo status=$?
212 # stdout: status=1
213
214 ### errexit test && and fail
215 set -o errexit
216 test -n X && false
217 echo status=$?
218 # stdout-json: ""
219 # status: 1
220
221 ### errexit and loop
222 set -o errexit
223 for x in 1 2 3; do
224 test $x = 2 && echo "hi $x"
225 done
226 # stdout: hi 2
227 # status: 1
228
229 ### errexit and brace group { }
230 set -o errexit
231 { test no = yes && echo hi; }
232 echo status=$?
233 # stdout: status=1
234
235 ### errexit and time { }
236 set -o errexit
237 time false
238 echo status=$?
239 # status: 1
240
241 ### errexit with !
242 set -o errexit
243 echo one
244 ! true
245 echo two
246 ! false
247 echo three
248 # stdout-json: "one\ntwo\nthree\n"
249 # status: 0
250
251 ### errexit with ! and ;
252 # AST has extra Sentence nodes; there was a REGRESSION here.
253 set -o errexit; echo one; ! true; echo two; ! false; echo three
254 # stdout-json: "one\ntwo\nthree\n"
255 # status: 0
256
257 ### errexit with while/until
258 set -o errexit
259 while false; do
260 echo ok
261 done
262 until false; do
263 echo ok # do this once then exit loop
264 break
265 done
266 # stdout: ok
267 # status: 0
268
269 ### errexit with (( ))
270 # from http://mywiki.wooledge.org/BashFAQ/105, this changed between verisons.
271 set -o errexit
272 i=0
273 (( i++ ))
274 echo done
275 # stdout-json: ""
276 # status: 1
277 # N-I dash status: 127
278 # N-I dash stdout-json: ""
279
280 ### errexit with subshell
281 set -o errexit
282 ( echo one; false; echo two; )
283 # stdout: one
284 # status: 1
285
286 ### errexit with command sub
287 # This is the bash-specific bug here:
288 # https://blogs.janestreet.com/when-bash-scripts-bite/
289 set -o errexit
290 s=$(echo one; false; echo two;)
291 echo "$s"
292 # stdout-json: ""
293 # status: 1
294 # BUG bash status: 0
295 # BUG bash stdout-json: "one\ntwo\n"
296
297 ### errexit with local
298 # I've run into this problem a lot.
299 # https://blogs.janestreet.com/when-bash-scripts-bite/
300 set -o errexit
301 f() {
302 echo good
303 local x=$(echo bad; false)
304 echo $x
305 }
306 f
307 # stdout-json: "good\n"
308 # status: 1
309 # BUG bash/dash/mksh stdout-json: "good\nbad\n"
310 # BUG bash/dash/mksh status: 0
311
312 ### setting errexit while it's being ignored
313 # ignored and then set again
314 set -o errexit
315 # osh aborts early here
316 if { echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4; }; then
317 echo 5;
318 fi
319 echo 6
320 false # this is the one that makes other shells fail
321 echo 7
322 # status: 1
323 # stdout-json: "1\n2\n"
324 # OK dash/bash/mksh stdout-json: "1\n2\n3\n4\n5\n6\n"
325
326 ### setting errexit in a subshell works but doesn't affect parent shell
327 ( echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4; )
328 echo 5
329 false
330 echo 6
331 # stdout-json: "1\n2\n3\n5\n6\n"
332 # status: 0
333
334 ### setting errexit while it's being ignored in a subshell
335 set -o errexit
336 if ( echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4 ); then
337 echo 5;
338 fi
339 echo 6 # This is executed because the subshell just returns false
340 false
341 echo 7
342 # status: 1
343 # stdout-json: "1\n2\n6\n"
344 # OK dash/bash/mksh stdout-json: "1\n2\n3\n4\n5\n6\n"
345
346 ### errexit double quard
347 # OSH bug fix. ErrExit needs a counter, not a boolean.
348 set -o errexit
349 if { ! false; false; true; } then
350 echo true
351 fi
352 false
353 echo done
354 # status: 1
355 # stdout-json: "true\n"
356
357 ### pipefail
358 # NOTE: the sleeps are because osh can fail non-deterministically because of a
359 # bug. Same problem as PIPESTATUS.
360 { sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; exit 0; }
361 echo $?
362 set -o pipefail
363 { sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; exit 0; }
364 echo $?
365 # stdout-json: "0\n2\n"
366 # status: 0
367 # N-I dash stdout-json: "0\n"
368 # N-I dash status: 2