1 # Test shell execution options.
2
3 #### simple_word_eval doesn't split, glob, or elide empty
4 mkdir mydir
5 touch foo.txt bar.txt spam.txt
6 spaces='a b'
7 dir=mydir
8 glob=*.txt
9 prefix=sp
10 set -- 'x y' z
11
12 for i in 1 2; do
13 local empty=
14 argv.py $spaces $glob $empty $prefix*.txt
15
16 # arrays still work too, with this weird rule
17 argv.py -"$@"-
18
19 shopt -s simple_word_eval
20 done
21 ## STDOUT:
22 ['a', 'b', 'bar.txt', 'foo.txt', 'spam.txt', 'spam.txt']
23 ['-x y', 'z-']
24 ['a b', '*.txt', '', 'spam.txt']
25 ['-x y', 'z-']
26 ## END
27
28 #### simple_word_eval and strict_array conflict over globs
29 touch foo.txt bar.txt
30 set -- f
31
32 argv.py "$@"*.txt
33 shopt -s simple_word_eval
34 argv.py "$@"*.txt
35 shopt -s strict_array
36 argv.py "$@"*.txt
37
38 ## status: 1
39 ## STDOUT:
40 ['foo.txt']
41 ['foo.txt']
42 ## END
43
44 #### simple_word_eval and glob
45 shopt -s simple_word_eval
46
47 # rm -v -f *.ff
48 touch 1.ff 2.ff
49
50 for i in *.ff; do
51 echo $i
52 done
53
54 array=(*.ff)
55 echo "${array[@]}"
56
57 echo *.ff
58
59 ## STDOUT:
60 1.ff
61 2.ff
62 1.ff 2.ff
63 1.ff 2.ff
64 ## END
65
66 #### parse_at
67 words=(a 'b c')
68 argv.py @words
69
70 shopt -s parse_at
71 argv.py @words
72
73 ## STDOUT:
74 ['@words']
75 ['a', 'b c']
76 ## END
77
78 #### parse_at can't be used outside top level
79 f() {
80 shopt -s parse_at
81 echo status=$?
82 }
83 f
84 echo 'should not get here'
85 ## status: 1
86 ## stdout-json: ""
87
88
89 #### sourcing a file that sets parse_at
90 cat >lib.sh <<EOF
91 shopt -s parse_at
92 echo lib.sh
93 EOF
94
95 words=(a 'b c')
96 argv.py @words
97
98 # This has a side effect, which is a bit weird, but not sure how to avoid it.
99 # Maybe we should say that libraries aren't allowed to change it?
100
101 source lib.sh
102 echo 'main.sh'
103
104 argv.py @words
105 ## STDOUT:
106 ['@words']
107 lib.sh
108 main.sh
109 ['a', 'b c']
110 ## END
111
112 #### parse_at can be specified through sh -O
113 $SH +O parse_at -c 'words=(a "b c"); argv.py @words'
114 $SH -O parse_at -c 'words=(a "b c"); argv.py @words'
115 ## STDOUT:
116 ['@words']
117 ['a', 'b c']
118 ## END
119
120 #### @a splices into $0
121 shopt -s simple_word_eval parse_at
122 a=(echo hi)
123 "${a[@]}"
124 @a
125
126 # Bug fix
127 shopt -s strict_array
128
129 "${a[@]}"
130 @a
131 ## STDOUT:
132 hi
133 hi
134 hi
135 hi
136 ## END
137
138 #### ARGV is alias for "$@"
139 shopt -s parse_at
140 argv.py "$@"
141 argv.py @ARGV
142 argv.py "${ARGV[@]}" # not useful, but it works!
143
144 set -- 'a b' c
145 argv.py "$@"
146 argv.py @ARGV
147
148 f() {
149 argv.py "$@"
150 argv.py @ARGV
151 }
152 f 1 '2 3'
153 ## STDOUT:
154 []
155 []
156 []
157 ['a b', 'c']
158 ['a b', 'c']
159 ['1', '2 3']
160 ['1', '2 3']
161 ## END
162
163 #### shopt -s strict:all
164 shopt -s strict:all
165 # normal option names
166 shopt -o -p | grep -- ' -o ' | grep -v hashall
167 shopt -p strict:all
168 ## STDOUT:
169 set -o errexit
170 set -o nounset
171 set -o pipefail
172 shopt -s errexit
173 shopt -s inherit_errexit
174 shopt -s nounset
175 shopt -s nullglob
176 shopt -s pipefail
177 shopt -s strict_argv
178 shopt -s strict_arith
179 shopt -s strict_array
180 shopt -s strict_control_flow
181 shopt -s strict_errexit
182 shopt -s strict_glob
183 shopt -s strict_nameref
184 shopt -s strict_tilde
185 shopt -s strict_word_eval
186 ## END
187
188 #### shopt -s oil:basic
189 shopt -s oil:basic
190 # normal option names
191 shopt -o -p | grep -- ' -o ' | grep -v hashall
192 shopt -p oil:basic
193 ## STDOUT:
194 set -o errexit
195 set -o nounset
196 set -o pipefail
197 shopt -s command_sub_errexit
198 shopt -u dashglob
199 shopt -s errexit
200 shopt -u expand_aliases
201 shopt -s inherit_errexit
202 shopt -s nounset
203 shopt -s nullglob
204 shopt -s parse_at
205 shopt -s parse_brace
206 shopt -s parse_paren
207 shopt -s parse_triple_dots
208 shopt -s parse_triple_quoted
209 shopt -s pipefail
210 shopt -s process_sub_fail
211 shopt -s simple_word_eval
212 shopt -s strict_argv
213 shopt -s strict_arith
214 shopt -s strict_array
215 shopt -s strict_control_flow
216 shopt -s strict_errexit
217 shopt -s strict_glob
218 shopt -s strict_nameref
219 shopt -s strict_tilde
220 shopt -s strict_word_eval
221 shopt -u xtrace_details
222 shopt -s xtrace_rich
223 ## END
224
225 #### osh -O oil:basic
226 $SH -O oil:basic -c 'var x = %(one two three); write @x'
227 ## STDOUT:
228 one
229 two
230 three
231 ## END
232
233 #### strict:all includes inherit_errexit
234 shopt -s strict:all
235 echo $(echo one; false; echo two)
236 ## STDOUT:
237 one
238 ## END
239
240 #### parse_set
241 x=init
242
243 set x=42
244 echo x=$x
245 echo argv "$@"
246
247 shopt -s parse_set
248 var x = 1
249 set x=42
250 builtin set --
251 echo x=$x
252 echo argv "$@"
253
254 ## STDOUT:
255 x=init
256 argv x=42
257 x=42
258 argv
259 ## END
260
261 #### parse_brace: bad block to assignment builtin
262 shopt -s oil:basic
263 # This is a fatal programming error. It's unlike passing an extra arg?
264 local x=y { echo 'bad block' }
265 echo status=$?
266 ## status: 1
267 ## stdout-json: ""
268
269 #### parse_brace: bad block to external program
270 shopt -s oil:basic
271 # This is a fatal programming error. It's unlike passing an extra arg?
272 ls { echo 'bad block' }
273 echo status=$?
274 ## status: 1
275 ## stdout-json: ""
276
277 #### parse_brace: cd { } in pipeline
278 shopt -s oil:basic
279 cd /tmp {
280 pwd
281 pwd
282 } | tr a-z A-Z
283 ## STDOUT:
284 /TMP
285 /TMP
286 ## END
287
288
289 #### parse_brace: if accepts blocks
290 shopt -s oil:basic
291 shopt -u errexit # don't need strict_errexit check!
292
293 if test -n foo {
294 echo one
295 }
296 # harder
297 if test -n foo; test -n bar {
298 echo two
299 }
300
301 # just like POSIX shell!
302 if test -n foo;
303
304 test -n bar {
305 echo three
306 }
307
308 if test -z foo {
309 echo if
310 } else {
311 echo else
312 }
313
314 if test -z foo {
315 echo if
316 } elif test -z '' {
317 echo elif
318 } else {
319 echo else
320 }
321
322 echo 'one line'
323 if test -z foo { echo if } elif test -z '' { echo 1 }; if test -n foo { echo 2 };
324
325 echo 'sh syntax'
326 if test -z foo; then echo if; elif test -z ''; then echo 1; fi; if test -n foo { echo 2 };
327
328 # NOTE: This is not alowed because it's like a brace group!
329 # if test -n foo; {
330
331 ## STDOUT:
332 one
333 two
334 three
335 else
336 elif
337 one line
338 1
339 2
340 sh syntax
341 1
342 2
343 ## END
344
345 #### parse_brace: brace group in if condition
346
347 # strict_errexit would make this a RUNTIME error
348 shopt -s parse_brace
349 if { echo one; echo two } {
350 echo three
351 }
352 ## STDOUT:
353 one
354 two
355 three
356 ## END
357
358 #### parse_brace: while/until
359 shopt -s oil:basic
360 while true {
361 echo one
362 break
363 }
364 while true { echo two; break }
365
366 echo 'sh syntax'
367 while true; do echo three; break; done
368 ## STDOUT:
369 one
370 two
371 sh syntax
372 three
373 ## END
374
375 #### parse_brace: for-in loop
376 shopt -s oil:basic
377 for x in one two {
378 echo $x
379 }
380 for x in three { echo $x }
381
382 echo 'sh syntax'
383 for x in four; do echo $x; done
384
385 ## STDOUT:
386 one
387 two
388 three
389 sh syntax
390 four
391 ## END
392
393 #### parse_brace case
394 shopt -s oil:basic
395
396 var files = %(foo.py 'foo test.sh')
397 for name in "${files[@]}" ; do
398 case $name in
399 *.py)
400 echo python
401 ;;
402 *.sh)
403 echo shell
404 ;;
405 esac
406 done
407
408 for name in @files {
409 case $name {
410 (*.py)
411 echo python
412 ;;
413 (*.sh) echo shell ;;
414 }
415 }
416
417 ## STDOUT:
418 python
419 shell
420 python
421 shell
422 ## END
423
424 #### parse_paren: if statement
425 shopt -s oil:basic
426 var x = 1
427 if (x < 42) {
428 echo less
429 }
430
431 if (x < 0) {
432 echo negative
433 } elif (x < 42) {
434 echo less
435 }
436
437 if (x < 0) {
438 echo negative
439 } elif (x < 1) {
440 echo less
441 } else {
442 echo other
443 }
444
445
446 ## STDOUT:
447 less
448 less
449 other
450 ## END
451
452 #### parse_paren: while statement
453 shopt -s oil:basic
454
455 # ksh style
456 var x = 1
457 while (( x < 3 )) {
458 echo $x
459 setvar x += 1
460 }
461 echo 'done ksh'
462
463 # sh style
464 var y = 1
465 while test $y -lt 3 {
466 echo $y
467 setvar y += 1
468 }
469 echo 'done sh'
470
471 # oil
472 var z = 1
473 while (z < 3) {
474 echo $z
475 setvar z += 1
476 }
477 echo 'done oil'
478
479 ## STDOUT:
480 1
481 2
482 done ksh
483 1
484 2
485 done sh
486 1
487 2
488 done oil
489 ## END
490
491 #### while subshell without parse_paren
492 while ( echo one ); do
493 echo two
494 break
495 done
496 ## STDOUT:
497 one
498 two
499 ## END
500
501 #### parse_paren: for loop
502 shopt -s oil:basic
503 var array = %(one two three)
504 for (item in array) {
505 echo $item
506 }
507
508 echo ---
509
510 declare -A A=([k]=v [k2]=v2) # iterate over keys
511 for (key in A) {
512 echo $key
513 } | sort
514 ## STDOUT:
515 one
516 two
517 three
518 ---
519 k
520 k2
521 ## END
522
523 #### parse_equals: allows bare assignment
524 shopt -s oil:all # including nice options
525 x = 1 + 2*3
526 echo $x
527 ## STDOUT:
528 7
529 ## END
530
531 #### parse_equals: disallows ENV=val mycommand
532 shopt -s oil:all
533 ENV=val echo hi
534 ## status: 2
535 ## stdout-json: ""
536
537 #### parse_equals: disallows var=val
538 shopt -s oil:all
539 var=val
540 ## status: 2
541 ## stdout-json: ""
542
543 #### parse_rawc: C strings in %() array literals
544 shopt -s oil:basic
545
546 # BUG: Surprising that this doesn't work because of command mode!
547 var lines=%(c'aa\tbb' c'cc\tdd')
548 echo @lines
549
550 ## STDOUT:
551 ## END
552
553
554 #### parse_paren allows f(x)
555 shopt -s parse_paren
556 func f(x) {
557 echo foo $x
558 }
559 f(42)
560 ## STDOUT:
561 foo 42
562 ## END
563
564 #### nullglob is on with oil:basic
565 write one *.zzz two
566 shopt -s oil:basic
567 write __
568 write one *.zzz two
569 ## STDOUT:
570 one
571 *.zzz
572 two
573 __
574 one
575 two
576 ## END
577
578 #### nullglob is on with oil:all
579 write one *.zzz two
580 shopt -s oil:all
581 write __
582 write one *.zzz two
583 ## STDOUT:
584 one
585 *.zzz
586 two
587 __
588 one
589 two
590 ## END
591
592 #### shopt -s simple_echo
593 foo='one two'
594 echo $foo # bad split then join
595 shopt -s simple_echo
596 echo
597 echo "$foo" # good
598 echo -e "$foo" # still good
599 echo $foo
600 ## status: 2
601 ## STDOUT:
602 one two
603
604 one two
605 one two
606 ## END
607
608 #### shopt -s dashglob
609 mkdir globdir
610 cd globdir
611
612 touch -- file -v
613
614 argv.py *
615
616 shopt -s oil:basic # turns OFF dashglob
617 argv.py *
618
619 shopt -s dashglob # turn it ON
620 argv.py *
621
622 ## STDOUT:
623 ['-v', 'file']
624 ['file']
625 ['-v', 'file']
626 ## END
627
628 #### shopt -s oil:basic turns some options on and others off
629 show() {
630 shopt -p | egrep 'dashglob|strict_arith'
631 }
632
633 show
634 echo ---
635
636 shopt -s strict_arith
637 show
638 echo ---
639
640 shopt -s oil:basic # strict_arith should still be on after this!
641 show
642 echo ---
643
644 shopt -u oil:basic # strict_arith should still be on after this!
645 show
646
647 ## STDOUT:
648 shopt -s dashglob
649 shopt -u strict_arith
650 ---
651 shopt -s dashglob
652 shopt -s strict_arith
653 ---
654 shopt -u dashglob
655 shopt -s strict_arith
656 ---
657 shopt -s dashglob
658 shopt -u strict_arith
659 ## END
660
661 #### oil:basic disables aliases
662
663 alias x='echo hi'
664 x
665
666 shopt --set oil:basic
667 shopt --unset errexit
668 x
669 echo status=$?
670
671 shopt --set expand_aliases
672 x
673
674 ## STDOUT:
675 hi
676 status=127
677 hi
678 ## END
679
680