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