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: huBs
19 ## OK mksh stdout: ush
20 ## N-I dash stdout-json: ""
21 ## N-I dash status: 2
22
23 #### $- with interactive shell
24 $SH -c 'echo $-' | grep i || echo FALSE
25 $SH -i -c 'echo $-' | grep -q i && echo TRUE
26 ## STDOUT:
27 FALSE
28 TRUE
29 ## END
30 #### pass short options on command line
31 $SH -e -c 'false; echo status=$?'
32 ## stdout-json: ""
33 ## status: 1
34
35 #### pass long options on command line
36 $SH -o errexit -c 'false; echo status=$?'
37 ## stdout-json: ""
38 ## status: 1
39
40 #### can continue after unknown option
41 # dash and mksh make this a fatal error no matter what.
42 set -o errexit
43 set -o STRICT || true # unknown option
44 echo hello
45 ## stdout: hello
46 ## status: 0
47 ## BUG dash/mksh stdout-json: ""
48 ## BUG dash status: 2
49 ## BUG mksh status: 1
50
51 #### set with both options and argv
52 set -o errexit a b c
53 echo "$@"
54 false
55 echo done
56 ## stdout: a b c
57 ## status: 1
58
59 #### set -o vi/emacs
60 set -o vi
61 echo $?
62 set -o emacs
63 echo $?
64 ## STDOUT:
65 0
66 0
67 ## END
68
69 #### nounset
70 echo "[$unset]"
71 set -o nounset
72 echo "[$unset]"
73 echo end # never reached
74 ## stdout: []
75 ## status: 1
76 ## OK dash status: 2
77
78 #### -u is nounset
79 echo "[$unset]"
80 set -u
81 echo "[$unset]"
82 echo end # never reached
83 ## stdout: []
84 ## status: 1
85 ## OK dash status: 2
86
87 #### nounset with "$@"
88 set a b c
89 set -u # shouldn't touch argv
90 echo "$@"
91 ## stdout: a b c
92
93 #### set -u -- clears argv
94 set a b c
95 set -u -- # shouldn't touch argv
96 echo "$@"
97 ## stdout:
98
99 #### set -u -- x y z
100 set a b c
101 set -u -- x y z
102 echo "$@"
103 ## stdout: x y z
104
105 #### reset option with long flag
106 set -o errexit
107 set +o errexit
108 echo "[$unset]"
109 ## stdout: []
110 ## status: 0
111
112 #### reset option with short flag
113 set -u
114 set +u
115 echo "[$unset]"
116 ## stdout: []
117 ## status: 0
118
119 #### set -eu (flag parsing)
120 set -eu
121 echo "[$unset]"
122 echo status=$?
123 ## stdout-json: ""
124 ## status: 1
125 ## OK dash status: 2
126
127 #### -n for no execution (useful with --ast-output)
128 # NOTE: set +n doesn't work because nothing is executed!
129 echo 1
130 set -n
131 echo 2
132 set +n
133 echo 3
134 # osh doesn't work because it only checks -n in bin/oil.py?
135 ## STDOUT:
136 1
137 ## END
138 ## status: 0
139
140 #### pipefail
141 # NOTE: the sleeps are because osh can fail non-deterministically because of a
142 # bug. Same problem as PIPESTATUS.
143 { sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; }
144 echo $?
145 set -o pipefail
146 { sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; }
147 echo $?
148 ## STDOUT:
149 0
150 2
151 ## END
152 ## status: 0
153 ## N-I dash STDOUT:
154 0
155 ## END
156 ## N-I dash status: 2
157
158 #### shopt -p -o prints 'set' options
159 shopt -po nounset
160 set -o nounset
161 shopt -po nounset
162 ## STDOUT:
163 set +o nounset
164 set -o nounset
165 ## END
166 ## N-I dash/mksh stdout-json: ""
167 ## N-I dash/mksh status: 127
168
169 #### shopt -p prints 'shopt' options
170 shopt -p nullglob
171 shopt -s nullglob
172 shopt -p nullglob
173 ## STDOUT:
174 shopt -u nullglob
175 shopt -s nullglob
176 ## END
177 ## N-I dash/mksh stdout-json: ""
178 ## N-I dash/mksh status: 127
179
180 #### shopt with no flags prints options
181 cd $TMP
182
183 # print specific options. OSH does it in a different format.
184 shopt nullglob failglob > one.txt
185 wc -l one.txt
186 grep -o nullglob one.txt
187 grep -o failglob one.txt
188
189 # print all options
190 shopt | grep nullglob | wc -l
191 ## STDOUT:
192 2 one.txt
193 nullglob
194 failglob
195 1
196 ## END
197 ## N-I dash/mksh STDOUT:
198 0 one.txt
199 0
200 ## END
201
202 #### noclobber off
203 set -o errexit
204 echo foo > $TMP/can-clobber
205 set +C
206 echo foo > $TMP/can-clobber
207 set +o noclobber
208 echo foo > $TMP/can-clobber
209 cat $TMP/can-clobber
210 ## stdout: foo
211
212 #### noclobber on
213 # Not implemented yet.
214 rm $TMP/no-clobber
215 set -C
216 echo foo > $TMP/no-clobber
217 echo $?
218 echo foo > $TMP/no-clobber
219 echo $?
220 ## stdout-json: "0\n1\n"
221 ## OK dash stdout-json: "0\n2\n"
222
223 #### SHELLOPTS is updated when options are changed
224 echo $SHELLOPTS | grep -q xtrace
225 echo $?
226 set -x
227 echo $SHELLOPTS | grep -q xtrace
228 echo $?
229 set +x
230 echo $SHELLOPTS | grep -q xtrace
231 echo $?
232 ## stdout-json: "1\n0\n1\n"
233 ## N-I dash/mksh stdout-json: "1\n1\n1\n"
234
235 #### SHELLOPTS is readonly
236 SHELLOPTS=x
237 echo status=$?
238 ## stdout: status=1
239 ## N-I dash/mksh stdout: status=0
240
241 # Setting a readonly variable in osh is a hard failure.
242 ## OK osh status: 1
243 ## OK osh stdout-json: ""
244
245 #### set -o lists options
246 # NOTE: osh doesn't use the same format yet.
247 set -o | grep -o noexec
248 ## STDOUT:
249 noexec
250 ## END
251
252 #### set without args lists variables
253 __GLOBAL=g
254 f() {
255 local __mylocal=L
256 local __OTHERLOCAL=L
257 __GLOBAL=mutated
258 set | grep '^__'
259 }
260 g() {
261 local __var_in_parent_scope=D
262 f
263 }
264 g
265 ## status: 0
266 ## STDOUT:
267 __GLOBAL='mutated'
268 __OTHERLOCAL='L'
269 __mylocal='L'
270 __var_in_parent_scope='D'
271 ## END
272 ## OK bash STDOUT:
273 __GLOBAL=mutated
274 __OTHERLOCAL=L
275 __mylocal=L
276 __var_in_parent_scope=D
277 ## END
278 ## OK mksh STDOUT:
279 __GLOBAL=mutated
280 __var_in_parent_scope=D
281 __OTHERLOCAL=L
282 __mylocal=L
283 ## END
284
285 #### 'set' and 'eval' round trip
286
287 # NOTE: not testing arrays and associative arrays!
288 _space='[ ]'
289 _whitespace=$'[\t\r\n]'
290 _sq="'single quotes'"
291 _backslash_dq="\\ \""
292 _unicode=$'[\u03bc]'
293
294 # Save the variables
295 varfile=$TMP/vars-$(basename $SH).txt
296
297 set | grep '^_' > "$varfile"
298
299 # Unset variables
300 unset _space _whitespace _sq _backslash_dq _unicode
301 echo [ $_space $_whitespace $_sq $_backslash_dq $_unicode ]
302
303 # Restore them
304
305 . $varfile
306 echo "Code saved to $varfile" 1>&2 # for debugging
307
308 test "$_space" = '[ ]' && echo OK
309 test "$_whitespace" = $'[\t\r\n]' && echo OK
310 test "$_sq" = "'single quotes'" && echo OK
311 test "$_backslash_dq" = "\\ \"" && echo OK
312 test "$_unicode" = $'[\u03bc]' && echo OK
313
314 ## STDOUT:
315 [ ]
316 OK
317 OK
318 OK
319 OK
320 OK
321 ## END
322
323 #### set without args and array variables (not in OSH)
324 declare -a __array
325 __array=(1 2 '3 4')
326 set | grep '^__'
327 ## STDOUT:
328 __array=([0]="1" [1]="2" [2]="3 4")
329 ## END
330 ## OK mksh STDOUT:
331 __array[0]=1
332 __array[1]=2
333 __array[2]='3 4'
334 ## N-I dash stdout-json: ""
335 ## N-I dash status: 2
336 ## N-I osh stdout-json: ""
337 ## N-I osh status: 1
338
339 #### set without args and assoc array variables (not in OSH)
340 typeset -A __assoc
341 __assoc['k e y']='v a l'
342 __assoc[a]=b
343 set | grep '^__'
344 ## STDOUT:
345 __assoc=(["k e y"]="v a l" [a]="b" )
346 ## END
347 ## N-I mksh stdout-json: ""
348 ## N-I mksh status: 1
349 ## N-I dash stdout-json: ""
350 ## N-I dash status: 1
351 ## N-I osh stdout-json: ""
352 ## N-I osh status: 1
353
354 #### shopt -q
355 shopt -q nullglob
356 echo nullglob=$?
357
358 # set it
359 shopt -s nullglob
360
361 shopt -q nullglob
362 echo nullglob=$?
363
364 shopt -q nullglob failglob
365 echo nullglob,failglob=$?
366
367 # set it
368 shopt -s failglob
369 shopt -q nullglob failglob
370 echo nullglob,failglob=$?
371
372 ## STDOUT:
373 nullglob=1
374 nullglob=0
375 nullglob,failglob=1
376 nullglob,failglob=0
377 ## END
378 ## N-I dash/mksh STDOUT:
379 nullglob=127
380 nullglob=127
381 nullglob,failglob=127
382 nullglob,failglob=127
383 ## END
384
385 #### shopt -q invalid
386 shopt -q invalidZZ
387 echo invalidZZ=$?
388 ## STDOUT:
389 invalidZZ=2
390 ## END
391 ## OK bash STDOUT:
392 invalidZZ=1
393 ## END
394 ## N-I dash/mksh STDOUT:
395 invalidZZ=127
396 ## END
397
398 #### shopt -s all:strict
399 n=2
400
401 show-strict() {
402 shopt -p | grep 'strict-' | head -n $n
403 echo -
404 }
405
406 show-strict
407 shopt -s all:strict
408 show-strict
409 shopt -u all:strict
410 show-strict
411 ## STDOUT:
412 shopt -u strict-argv
413 shopt -s strict-arith
414 -
415 shopt -s strict-argv
416 shopt -s strict-arith
417 -
418 shopt -u strict-argv
419 shopt -u strict-arith
420 -
421 ## END
422 ## N-I dash status: 2
423 ## N-I dash stdout-json: ""
424 ## N-I bash/mksh STDOUT:
425 -
426 -
427 -
428 ## END
429
430 #### shopt allows for backward compatibility like bash
431
432 # doesn't have to be on, but just for testing
433 set -o errexit
434
435 shopt -p nullglob || true # bash returns 1 here? Like -q.
436
437 # This should set strict-array, and return 1, which can be ignored
438 shopt -s nullglob strict-OPTION_NOT_YET_IMPLEMENTED 2>/dev/null || true
439 echo status=$?
440
441 shopt -p nullglob || true
442
443 ## STDOUT:
444 shopt -u nullglob
445 status=0
446 shopt -s nullglob
447 ## END
448 ## N-I dash/mksh STDOUT:
449 status=0
450 ## END
451 ## N-I dash/mksh status: 0
452
453 #### shopt -p validates option names
454 shopt -p nullglob invalid failglob
455 echo status=$?
456 # same thing as -p, slightly different format in bash
457 shopt nullglob invalid failglob > $TMP/out.txt
458 status=$?
459 sed --regexp-extended 's/\s+/ /' $TMP/out.txt # make it easier to assert
460 echo status=$status
461 ## STDOUT:
462 shopt -u nullglob
463 status=2
464 shopt -u nullglob
465 status=2
466 ## END
467 ## OK bash STDOUT:
468 shopt -u nullglob
469 shopt -u failglob
470 status=1
471 nullglob off
472 failglob off
473 status=1
474 ## END
475 ## N-I dash/mksh STDOUT:
476 status=127
477 status=127
478 ## END
479
480 #### shopt -p -o validates option names
481 shopt -p -o errexit invalid nounset
482 echo status=$?
483 ## STDOUT:
484 set +o errexit
485 status=2
486 ## END
487 ## OK bash STDOUT:
488 set +o errexit
489 set +o nounset
490 status=1
491 ## END
492 ## N-I dash/mksh STDOUT:
493 status=127
494 ## END