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