1 # Test shell execution options.
2
3 #### simple_word_eval doesn't split, glob, or elide empty
4 mkdir mydir
5 touch foo.z bar.z spam.z
6 spaces='a b'
7 dir=mydir
8 glob=*.z
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*.z
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.z', 'foo.z', 'spam.z', 'spam.z']
23 ['-x y', 'z-']
24 ['a b', '*.z', '', 'spam.z']
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 shopt -s strict_argv
170 shopt -s strict_arith
171 shopt -s strict_array
172 shopt -s strict_control_flow
173 shopt -s strict_errexit
174 shopt -s strict_glob
175 shopt -s strict_nameref
176 shopt -s strict_tilde
177 shopt -s strict_word_eval
178 ## END
179
180 #### shopt -s oil:upgrade
181 shopt -s oil:upgrade
182 # normal option names
183 shopt -o -p | grep -- ' -o ' | grep -v hashall
184 shopt -p oil:upgrade
185 ## STDOUT:
186 set -o errexit
187 set -o nounset
188 set -o pipefail
189 shopt -s command_sub_errexit
190 shopt -u dashglob
191 shopt -s errexit
192 shopt -u expand_aliases
193 shopt -s inherit_errexit
194 shopt -s nounset
195 shopt -s nullglob
196 shopt -s parse_at
197 shopt -s parse_brace
198 shopt -s parse_equals
199 shopt -s parse_paren
200 shopt -s parse_proc
201 shopt -s parse_raw_string
202 shopt -s parse_triple_quote
203 shopt -s pipefail
204 shopt -s process_sub_fail
205 shopt -u redefine_proc
206 shopt -s sigpipe_status_ok
207 shopt -s simple_word_eval
208 shopt -s verbose_errexit
209 shopt -u xtrace_details
210 shopt -s xtrace_rich
211 ## END
212
213 #### osh -O oil:upgrade
214 $SH -O oil:upgrade -c 'var x = %(one two three); write @x'
215 ## STDOUT:
216 one
217 two
218 three
219 ## END
220
221 #### osh -O errexit: use -O everywhere, even for Bourne options
222 $SH -O errexit -c 'shopt -p -o errexit'
223 #$SH -O errexit -c 'shopt -p errexit' # bash doesn't allow this, but Oil does
224 ## STDOUT:
225 set -o errexit
226 ## END
227
228 #### osh -O invalid
229 $SH -O errexit -c 'echo hi'
230 echo status=$?
231 $SH -O invalid -c 'echo hi'
232 echo status=$?
233 ## STDOUT:
234 hi
235 status=0
236 status=2
237 ## END
238
239 #### osh -o new_option is also accepted
240
241 $SH -o nullglob -c 'echo nullglob'
242 echo $? flag nullglob
243
244 $SH -o oil:upgrade -c 'proc p { echo upgrade }; p'
245 echo $? flag oil:upgrade
246
247 # Should disallow these
248
249 set -o nullglob
250 echo $? set builtin nullglob
251 set -o oil:upgrade
252 echo $? set builtin oil:upgrade
253
254 ## STDOUT:
255 nullglob
256 0 flag nullglob
257 upgrade
258 0 flag oil:upgrade
259 2 set builtin nullglob
260 2 set builtin oil:upgrade
261 ## END
262
263
264 #### oil:upgrade includes inherit_errexit
265 shopt -s oil:upgrade
266 echo $(echo one; false; echo two)
267 ## status: 1
268 ## stdout-json: ""
269
270 #### parse_brace: bad block to assignment builtin
271 shopt -s oil:upgrade
272 # This is a fatal programming error. It's unlike passing an extra arg?
273 local x=y { echo 'bad block' }
274 echo status=$?
275 ## status: 1
276 ## stdout-json: ""
277
278 #### parse_brace: bad block to external program
279 shopt -s oil:upgrade
280 # This is a fatal programming error. It's unlike passing an extra arg?
281 ls { echo 'bad block' }
282 echo status=$?
283 ## status: 1
284 ## stdout-json: ""
285
286 #### parse_brace: cd { } in pipeline
287 shopt -s oil:upgrade
288 cd /tmp {
289 pwd
290 pwd
291 } | tr a-z A-Z
292 ## STDOUT:
293 /TMP
294 /TMP
295 ## END
296
297
298 #### parse_brace: if accepts blocks
299 shopt -s oil:upgrade
300 shopt -u errexit # don't need strict_errexit check!
301
302 if test -n foo {
303 echo one
304 }
305 # harder
306 if test -n foo; test -n bar {
307 echo two
308 }
309
310 # just like POSIX shell!
311 if test -n foo;
312
313 test -n bar {
314 echo three
315 }
316
317 if test -z foo {
318 echo if
319 } else {
320 echo else
321 }
322
323 if test -z foo {
324 echo if
325 } elif test -z '' {
326 echo elif
327 } else {
328 echo else
329 }
330
331 echo 'one line'
332 if test -z foo { echo if } elif test -z '' { echo 1 }; if test -n foo { echo 2 };
333
334 echo 'sh syntax'
335 if test -z foo; then echo if; elif test -z ''; then echo 1; fi; if test -n foo { echo 2 };
336
337 # NOTE: This is not alowed because it's like a brace group!
338 # if test -n foo; {
339
340 ## STDOUT:
341 one
342 two
343 three
344 else
345 elif
346 one line
347 1
348 2
349 sh syntax
350 1
351 2
352 ## END
353
354 #### parse_brace: brace group in if condition
355
356 # strict_errexit would make this a RUNTIME error
357 shopt -s parse_brace
358 if { echo one; echo two } {
359 echo three
360 }
361 ## STDOUT:
362 one
363 two
364 three
365 ## END
366
367 #### parse_brace: while/until
368 shopt -s oil:upgrade
369 while true {
370 echo one
371 break
372 }
373 while true { echo two; break }
374
375 echo 'sh syntax'
376 while true; do echo three; break; done
377 ## STDOUT:
378 one
379 two
380 sh syntax
381 three
382 ## END
383
384 #### parse_brace: for-in loop
385 shopt -s oil:upgrade
386 for x in one two {
387 echo $x
388 }
389 for x in three { echo $x }
390
391 echo 'sh syntax'
392 for x in four; do echo $x; done
393
394 ## STDOUT:
395 one
396 two
397 three
398 sh syntax
399 four
400 ## END
401
402 #### parse_brace case
403 shopt -s oil:upgrade
404
405 var files = %(foo.py 'foo test.sh')
406 for name in "${files[@]}" ; do
407 case $name in
408 *.py)
409 echo python
410 ;;
411 *.sh)
412 echo shell
413 ;;
414 esac
415 done
416
417 for name in @files {
418 case (name) {
419 *.py {
420 echo python
421 }
422 *.sh { echo shell }
423 }
424 }
425
426 ## STDOUT:
427 python
428 shell
429 python
430 shell
431 ## END
432
433 #### parse_paren: if statement
434 shopt -s oil:upgrade
435 var x = 1
436 if (x < 42) {
437 echo less
438 }
439
440 if (x < 0) {
441 echo negative
442 } elif (x < 42) {
443 echo less
444 }
445
446 if (x < 0) {
447 echo negative
448 } elif (x < 1) {
449 echo less
450 } else {
451 echo other
452 }
453
454
455 ## STDOUT:
456 less
457 less
458 other
459 ## END
460
461 #### parse_paren: while statement
462 shopt -s oil:upgrade
463
464 # ksh style
465 var x = 1
466 while (( x < 3 )) {
467 echo $x
468 setvar x += 1
469 }
470 echo 'done ksh'
471
472 # sh style
473 var y = 1
474 while test $y -lt 3 {
475 echo $y
476 setvar y += 1
477 }
478 echo 'done sh'
479
480 # oil
481 var z = 1
482 while (z < 3) {
483 echo $z
484 setvar z += 1
485 }
486 echo 'done oil'
487
488 ## STDOUT:
489 1
490 2
491 done ksh
492 1
493 2
494 done sh
495 1
496 2
497 done oil
498 ## END
499
500 #### while subshell without parse_paren
501 while ( echo one ); do
502 echo two
503 break
504 done
505 ## STDOUT:
506 one
507 two
508 ## END
509
510 #### nullglob is on with oil:upgrade
511 write one *.zzz two
512 shopt -s oil:upgrade
513 write __
514 write one *.zzz two
515 ## STDOUT:
516 one
517 *.zzz
518 two
519 __
520 one
521 two
522 ## END
523
524 #### nullglob is on with oil:all
525 write one *.zzz two
526 shopt -s oil:all
527 write __
528 write one *.zzz two
529 ## STDOUT:
530 one
531 *.zzz
532 two
533 __
534 one
535 two
536 ## END
537
538 #### shopt -s simple_echo
539 foo='one two'
540 echo $foo # bad split then join
541 shopt -s simple_echo
542 echo
543 echo "$foo" # good
544 echo $foo
545
546 echo -e "$foo" # -e isn't special!
547 echo -n "$foo" # -n isn't special!
548
549 ## STDOUT:
550 one two
551
552 one two
553 one two
554 -e one two
555 -n one two
556 ## END
557
558 #### shopt -s dashglob
559 mkdir globdir
560 cd globdir
561
562 touch -- file -v
563
564 argv.py *
565
566 shopt -s oil:upgrade # turns OFF dashglob
567 argv.py *
568
569 shopt -s dashglob # turn it ON
570 argv.py *
571
572 ## STDOUT:
573 ['-v', 'file']
574 ['file']
575 ['-v', 'file']
576 ## END
577
578 #### shopt -s oil:upgrade turns some options on and others off
579 show() {
580 shopt -p | egrep 'dashglob|simple_word_eval'
581 }
582
583 show
584 echo ---
585
586 shopt -s simple_word_eval
587 show
588 echo ---
589
590 shopt -s oil:upgrade # strict_arith should still be on after this!
591 show
592 echo ---
593
594 shopt -u oil:upgrade # strict_arith should still be on after this!
595 show
596
597 ## STDOUT:
598 shopt -s dashglob
599 shopt -u simple_word_eval
600 ---
601 shopt -s dashglob
602 shopt -s simple_word_eval
603 ---
604 shopt -u dashglob
605 shopt -s simple_word_eval
606 ---
607 shopt -s dashglob
608 shopt -u simple_word_eval
609 ## END
610
611 #### oil:upgrade disables aliases
612
613 alias x='echo hi'
614 x
615
616 shopt --set oil:upgrade
617 shopt --unset errexit
618 x
619 echo status=$?
620
621 shopt --set expand_aliases
622 x
623
624 ## STDOUT:
625 hi
626 status=127
627 hi
628 ## END
629
630 #### sigpipe_status_ok
631
632 status_141() {
633 return 141
634 }
635
636 yes | head -n 1
637 echo ${PIPESTATUS[@]}
638
639 # DUMMY
640 yes | status_141
641 echo ${PIPESTATUS[@]}
642
643 shopt --set oil:upgrade # sigpipe_status_ok
644 shopt --unset errexit
645
646 yes | head -n 1
647 echo ${PIPESTATUS[@]}
648
649 # Conveniently, the last 141 isn't changed to 0, because it's run in the
650 # CURRENT process.
651
652 yes | status_141
653 echo ${PIPESTATUS[@]}
654
655 echo background
656 false | status_141 &
657 wait
658 echo status=$? pipestatus=${PIPESTATUS[@]}
659
660 ## STDOUT:
661 y
662 141 0
663 141 141
664 y
665 0 0
666 0 141
667 background
668 status=0 pipestatus=0 141
669 ## END
670
671
672 #### printf | head regression (sigpipe_status_ok)
673
674 shopt --set oil:upgrade
675 shopt --unset errexit
676
677 bad() {
678 /usr/bin/printf '%65538s\n' foo | head -c 1
679 echo external on ${_pipeline_status[@]}
680
681 shopt --unset sigpipe_status_ok {
682 /usr/bin/printf '%65538s\n' foo | head -c 1
683 }
684 echo external off ${_pipeline_status[@]}
685
686 printf '%65538s\n' foo | head -c 1
687 echo builtin on ${_pipeline_status[@]}
688
689 shopt --unset sigpipe_status_ok {
690 printf '%65538s\n' foo | head -c 1
691 }
692 echo builtin off ${_pipeline_status[@]}
693 }
694
695 bad
696 echo finished
697
698 ## STDOUT:
699 external on 0 0
700 external off 141 0
701 builtin on 0 0
702 builtin off 141 0
703 finished
704 ## END
705
706 #### redefine_proc for shell functions
707
708 f() {
709 echo 1
710 }
711 echo 'first'
712
713 f() {
714 echo 2
715 }
716 echo 'second'
717
718 shopt --set oil:upgrade
719 f() {
720 echo 3
721 }
722 echo 'third'
723 ## STDOUT:
724 first
725 second
726 ## END
727 ## status: 1
728
729 #### redefine_proc for procs
730 shopt --set parse_proc
731
732 proc p {
733 echo 1
734 }
735 echo 'first'
736
737 proc p {
738 echo 2
739 }
740 echo 'second'
741
742 shopt --set oil:upgrade
743 proc p {
744 echo 3
745 }
746 echo 'third'
747 ## STDOUT:
748 first
749 second
750 ## END
751 ## status: 1
752
753 #### redefine_proc is on in interactive shell
754
755 $SH -O oil:all -i --rcfile /dev/null -c "
756 source $REPO_ROOT/spec/testdata/module/common.oil
757 source $REPO_ROOT/spec/testdata/module/redefinition.oil
758 log hi
759 "
760 ## STDOUT:
761 common
762 redefinition
763 ## END
764 ## STDERR:
765 hi
766 ## END
767
768
769 #### redefine_module is on in interactive shell
770
771 $SH -O oil:all -i --rcfile /dev/null -c "
772 source $REPO_ROOT/spec/testdata/module/common.oil
773 source $REPO_ROOT/spec/testdata/module/common.oil
774 log hi
775 " 2>stderr.txt
776 echo status=$?
777
778 # Make sure there are two lines
779 wc -l stderr.txt
780 ## STDOUT:
781 common
782 common
783 status=0
784 2 stderr.txt
785 ## END
786
787
788 #### parse options in sourced file (bug #1628)
789
790 set -e # catch errors
791
792 alias e=echo
793 shopt -u expand_aliases
794
795 source $REPO_ROOT/spec/testdata/parse_opts.sh a b c
796
797 echo OK
798
799 # alias persists
800 e alias on
801
802 # parse_paren doesn't persist
803 #if (x > 1) {
804 # echo 'OK'
805 #}
806
807 FOO=bar source $REPO_ROOT/spec/testdata/parse_opts.sh
808 echo OK
809
810
811 ## STDOUT:
812 OK
813 alias on
814 OK
815 ## END