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