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_raw_string
208 shopt -s parse_triple_dot
209 shopt -s parse_triple_quote
210 shopt -s pipefail
211 shopt -s process_sub_fail
212 shopt -u redefine_proc
213 shopt -s sigpipe_status_ok
214 shopt -s simple_word_eval
215 shopt -s strict_argv
216 shopt -s strict_arith
217 shopt -s strict_array
218 shopt -s strict_control_flow
219 shopt -s strict_errexit
220 shopt -s strict_glob
221 shopt -s strict_nameref
222 shopt -s strict_tilde
223 shopt -s strict_word_eval
224 shopt -u xtrace_details
225 shopt -s xtrace_rich
226 ## END
227
228 #### osh -O oil:basic
229 $SH -O oil:basic -c 'var x = %(one two three); write @x'
230 ## STDOUT:
231 one
232 two
233 three
234 ## END
235
236 #### strict:all includes inherit_errexit
237 shopt -s strict:all
238 echo $(echo one; false; echo two)
239 ## STDOUT:
240 one
241 ## END
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 #### parse_paren allows f(x)
526 shopt -s parse_paren
527 func f(x) {
528 echo foo $x
529 }
530 f(42)
531 ## STDOUT:
532 foo 42
533 ## END
534
535 #### nullglob is on with oil:basic
536 write one *.zzz two
537 shopt -s oil:basic
538 write __
539 write one *.zzz two
540 ## STDOUT:
541 one
542 *.zzz
543 two
544 __
545 one
546 two
547 ## END
548
549 #### nullglob is on with oil:all
550 write one *.zzz two
551 shopt -s oil:all
552 write __
553 write one *.zzz two
554 ## STDOUT:
555 one
556 *.zzz
557 two
558 __
559 one
560 two
561 ## END
562
563 #### shopt -s simple_echo
564 foo='one two'
565 echo $foo # bad split then join
566 shopt -s simple_echo
567 echo
568 echo "$foo" # good
569 echo -e "$foo" # still good
570 echo $foo
571 ## status: 2
572 ## STDOUT:
573 one two
574
575 one two
576 one two
577 ## END
578
579 #### shopt -s dashglob
580 mkdir globdir
581 cd globdir
582
583 touch -- file -v
584
585 argv.py *
586
587 shopt -s oil:basic # turns OFF dashglob
588 argv.py *
589
590 shopt -s dashglob # turn it ON
591 argv.py *
592
593 ## STDOUT:
594 ['-v', 'file']
595 ['file']
596 ['-v', 'file']
597 ## END
598
599 #### shopt -s oil:basic turns some options on and others off
600 show() {
601 shopt -p | egrep 'dashglob|strict_arith'
602 }
603
604 show
605 echo ---
606
607 shopt -s strict_arith
608 show
609 echo ---
610
611 shopt -s oil:basic # strict_arith should still be on after this!
612 show
613 echo ---
614
615 shopt -u oil:basic # strict_arith should still be on after this!
616 show
617
618 ## STDOUT:
619 shopt -s dashglob
620 shopt -u strict_arith
621 ---
622 shopt -s dashglob
623 shopt -s strict_arith
624 ---
625 shopt -u dashglob
626 shopt -s strict_arith
627 ---
628 shopt -s dashglob
629 shopt -u strict_arith
630 ## END
631
632 #### oil:basic disables aliases
633
634 alias x='echo hi'
635 x
636
637 shopt --set oil:basic
638 shopt --unset errexit
639 x
640 echo status=$?
641
642 shopt --set expand_aliases
643 x
644
645 ## STDOUT:
646 hi
647 status=127
648 hi
649 ## END
650
651 #### sigpipe_status_ok
652
653 status_141() {
654 return 141
655 }
656
657 yes | head -n 1
658 echo ${PIPESTATUS[@]}
659
660 # DUMMY
661 yes | status_141
662 echo ${PIPESTATUS[@]}
663
664 shopt --set oil:basic # sigpipe_status_ok
665 shopt --unset errexit
666
667 yes | head -n 1
668 echo ${PIPESTATUS[@]}
669
670 # Conveniently, the last 141 isn't changed to 0, because it's run in the
671 # CURRENT process.
672
673 yes | status_141
674 echo ${PIPESTATUS[@]}
675
676 echo background
677 false | status_141 &
678 wait
679 echo status=$? pipestatus=${PIPESTATUS[@]}
680
681 ## STDOUT:
682 y
683 141 0
684 141 141
685 y
686 0 0
687 0 141
688 background
689 status=0 pipestatus=0 141
690 ## END
691
692
693 #### printf | head regression (sigpipe_status_ok)
694
695 shopt --set oil:basic
696 shopt --unset errexit
697
698 bad() {
699 /usr/bin/printf '%65538s\n' foo | head -c 1
700 echo external on ${_pipeline_status[@]}
701
702 shopt --unset sigpipe_status_ok {
703 /usr/bin/printf '%65538s\n' foo | head -c 1
704 }
705 echo external off ${_pipeline_status[@]}
706
707 printf '%65538s\n' foo | head -c 1
708 echo builtin on ${_pipeline_status[@]}
709
710 shopt --unset sigpipe_status_ok {
711 printf '%65538s\n' foo | head -c 1
712 }
713 echo builtin off ${_pipeline_status[@]}
714 }
715
716 bad
717 echo finished
718
719 ## STDOUT:
720 external on 0 0
721 external off 141 0
722 builtin on 0 0
723 builtin off 141 0
724 finished
725 ## END
726
727 #### redefine_proc for shell functions
728
729 f() {
730 echo 1
731 }
732 echo 'first'
733
734 f() {
735 echo 2
736 }
737 echo 'second'
738
739 shopt --set oil:basic
740 f() {
741 echo 3
742 }
743 echo 'third'
744 ## STDOUT:
745 first
746 second
747 ## END
748 ## status: 1
749
750 #### redefine_proc for procs
751
752 proc p {
753 echo 1
754 }
755 echo 'first'
756
757 proc p {
758 echo 2
759 }
760 echo 'second'
761
762 shopt --set oil:basic
763 proc p {
764 echo 3
765 }
766 echo 'third'
767 ## STDOUT:
768 first
769 second
770 ## END
771 ## status: 1
772
773 #### redefine_proc is on in interactive shell
774
775 $SH -O oil:all -i --rcfile /dev/null -c "
776 source $REPO_ROOT/spec/testdata/module/common.oil
777 source $REPO_ROOT/spec/testdata/module/redefinition.oil
778 log hi
779 "
780 ## STDOUT:
781 common
782 redefinition
783 ## END
784 ## STDERR:
785 hi
786 ## END
787
788
789 #### redefine_module is on in interactive shell
790
791 $SH -O oil:all -i --rcfile /dev/null -c "
792 source $REPO_ROOT/spec/testdata/module/common.oil
793 source $REPO_ROOT/spec/testdata/module/common.oil
794 log hi
795 "
796 ## STDOUT:
797 common
798 common
799 ## END
800 ## STDERR:
801 [oil -i] Reloading module 'common'
802 hi
803 ## END