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