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