1 ## oils_failures_allowed: 1
2
3 # Test shell execution options.
4
5 #### simple_word_eval doesn't split, glob, or elide empty
6 mkdir mydir
7 touch foo.z bar.z spam.z
8 spaces='a b'
9 dir=mydir
10 glob=*.z
11 prefix=sp
12 set -- 'x y' z
13
14 for i in 1 2; do
15 local empty=
16 argv.py $spaces $glob $empty $prefix*.z
17
18 # arrays still work too, with this weird rule
19 argv.py -"$@"-
20
21 shopt -s simple_word_eval
22 done
23 ## STDOUT:
24 ['a', 'b', 'bar.z', 'foo.z', 'spam.z', 'spam.z']
25 ['-x y', 'z-']
26 ['a b', '*.z', '', 'spam.z']
27 ['-x y', 'z-']
28 ## END
29
30 #### simple_word_eval and strict_array conflict over globs
31 touch foo.txt bar.txt
32 set -- f
33
34 argv.py "$@"*.txt
35 shopt -s simple_word_eval
36 argv.py "$@"*.txt
37 shopt -s strict_array
38 argv.py "$@"*.txt
39
40 ## status: 1
41 ## STDOUT:
42 ['foo.txt']
43 ['foo.txt']
44 ## END
45
46 #### simple_word_eval and glob
47 shopt -s simple_word_eval
48
49 # rm -v -f *.ff
50 touch 1.ff 2.ff
51
52 for i in *.ff; do
53 echo $i
54 done
55
56 array=(*.ff)
57 echo "${array[@]}"
58
59 echo *.ff
60
61 ## STDOUT:
62 1.ff
63 2.ff
64 1.ff 2.ff
65 1.ff 2.ff
66 ## END
67
68 #### parse_at
69 words=(a 'b c')
70 argv.py @words
71
72 shopt -s parse_at
73 argv.py @words
74
75 ## STDOUT:
76 ['@words']
77 ['a', 'b c']
78 ## END
79
80 #### DISABLED: parse_at can't be used outside top level
81
82 # shopt -u expand_aliases conflicted with ble.sh, and it was also broken for
83 # proc/func
84
85 f() {
86 shopt -s parse_at
87 echo status=$?
88 }
89 f
90 echo 'should not get here'
91 ## status: 1
92 ## stdout-json: ""
93
94
95 #### sourcing a file that sets parse_at
96 cat >lib.sh <<EOF
97 shopt -s parse_at
98 echo lib.sh
99 EOF
100
101 words=(a 'b c')
102 argv.py @words
103
104 # This has a side effect, which is a bit weird, but not sure how to avoid it.
105 # Maybe we should say that libraries aren't allowed to change it?
106
107 source lib.sh
108 echo 'main.sh'
109
110 argv.py @words
111 ## STDOUT:
112 ['@words']
113 lib.sh
114 main.sh
115 ['a', 'b c']
116 ## END
117
118 #### parse_at can be specified through sh -O
119 $SH +O parse_at -c 'words=(a "b c"); argv.py @words'
120 $SH -O parse_at -c 'words=(a "b c"); argv.py @words'
121 ## STDOUT:
122 ['@words']
123 ['a', 'b c']
124 ## END
125
126 #### @a splices into $0
127 shopt -s simple_word_eval parse_at
128 a=(echo hi)
129 "${a[@]}"
130 @a
131
132 # Bug fix
133 shopt -s strict_array
134
135 "${a[@]}"
136 @a
137 ## STDOUT:
138 hi
139 hi
140 hi
141 hi
142 ## END
143
144 #### shopt -s strict:all
145 shopt -s strict:all
146 # normal option names
147 shopt -o -p | grep -- ' -o ' | grep -v hashall
148 shopt -p strict:all
149 ## STDOUT:
150 shopt -s strict_argv
151 shopt -s strict_arith
152 shopt -s strict_array
153 shopt -s strict_control_flow
154 shopt -s strict_env_binding
155 shopt -s strict_errexit
156 shopt -s strict_glob
157 shopt -s strict_nameref
158 shopt -s strict_parse_equals
159 shopt -s strict_parse_slice
160 shopt -s strict_tilde
161 shopt -s strict_word_eval
162 ## END
163
164 #### shopt -s ysh:upgrade
165 shopt -s ysh:upgrade
166 # normal option names
167 shopt -o -p | grep -- ' -o ' | grep -v hashall
168 shopt -p ysh:upgrade
169 ## STDOUT:
170 set -o errexit
171 set -o nounset
172 set -o pipefail
173 shopt -s command_sub_errexit
174 shopt -u dashglob
175 shopt -s env_obj
176 shopt -s errexit
177 shopt -s for_loop_frames
178 shopt -s inherit_errexit
179 shopt -s init_ysh_globals
180 shopt -s nounset
181 shopt -s nullglob
182 shopt -s parse_at
183 shopt -s parse_brace
184 shopt -s parse_bracket
185 shopt -s parse_equals
186 shopt -s parse_func
187 shopt -s parse_paren
188 shopt -s parse_proc
189 shopt -s parse_triple_quote
190 shopt -s parse_ysh_string
191 shopt -s pipefail
192 shopt -s process_sub_fail
193 shopt -s sigpipe_status_ok
194 shopt -s simple_word_eval
195 shopt -s verbose_errexit
196 shopt -s verbose_warn
197 shopt -u xtrace_details
198 shopt -s xtrace_rich
199 ## END
200
201 #### osh -O ysh:upgrade
202 $SH -O ysh:upgrade -c 'var x = :|one two three|; write @x'
203 ## STDOUT:
204 one
205 two
206 three
207 ## END
208
209 #### osh -O errexit: use -O everywhere, even for Bourne options
210 $SH -O errexit -c 'shopt -p -o errexit'
211 #$SH -O errexit -c 'shopt -p errexit' # bash doesn't allow this, but Oil does
212 ## STDOUT:
213 set -o errexit
214 ## END
215
216 #### osh -O invalid
217 $SH -O errexit -c 'echo hi'
218 echo status=$?
219 $SH -O invalid -c 'echo hi'
220 echo status=$?
221 ## STDOUT:
222 hi
223 status=0
224 status=2
225 ## END
226
227 #### osh -o new_option is also accepted
228
229 $SH -o nullglob -c 'echo nullglob'
230 echo $? flag nullglob
231
232 $SH -o oil:upgrade -c 'proc p { echo upgrade }; p'
233 echo $? flag oil:upgrade
234
235 # Should disallow these
236
237 set -o nullglob
238 echo $? set builtin nullglob
239 set -o oil:upgrade
240 echo $? set builtin oil:upgrade
241
242 ## STDOUT:
243 nullglob
244 0 flag nullglob
245 upgrade
246 0 flag oil:upgrade
247 2 set builtin nullglob
248 2 set builtin oil:upgrade
249 ## END
250
251
252 #### oil:upgrade includes inherit_errexit
253 shopt -s oil:upgrade
254 echo $(echo one; false; echo two)
255 ## status: 1
256 ## stdout-json: ""
257
258 #### parse_brace: bad block to assignment builtin
259 shopt -s oil:upgrade
260 # This is a fatal programming error. It's unlike passing an extra arg?
261 local x=y { echo 'bad block' }
262 echo status=$?
263 ## status: 1
264 ## stdout-json: ""
265
266 #### parse_brace: bad block to external program
267 shopt -s oil:upgrade
268 # This is a fatal programming error. It's unlike passing an extra arg?
269 ls { echo 'bad block' }
270 echo status=$?
271 ## status: 1
272 ## stdout-json: ""
273
274 #### parse_brace: cd { } in pipeline
275 shopt -s oil:upgrade
276 cd /tmp {
277 pwd
278 pwd
279 } | tr a-z A-Z
280 ## STDOUT:
281 /TMP
282 /TMP
283 ## END
284
285
286 #### parse_brace: if accepts blocks
287 shopt -s oil:upgrade
288 shopt -u errexit # don't need strict_errexit check!
289
290 if test -n foo {
291 echo one
292 }
293 # harder
294 if test -n foo; test -n bar {
295 echo two
296 }
297
298 # just like POSIX shell!
299 if test -n foo;
300
301 test -n bar {
302 echo three
303 }
304
305 if test -z foo {
306 echo if
307 } else {
308 echo else
309 }
310
311 if test -z foo {
312 echo if
313 } elif test -z '' {
314 echo elif
315 } else {
316 echo else
317 }
318
319 echo 'one line'
320 if test -z foo { echo if } elif test -z '' { echo 1 }; if test -n foo { echo 2 };
321
322 echo 'sh syntax'
323 if test -z foo; then echo if; elif test -z ''; then echo 1; fi; if test -n foo { echo 2 };
324
325 # NOTE: This is not allowed because it's like a brace group!
326 # if test -n foo; {
327
328 ## STDOUT:
329 one
330 two
331 three
332 else
333 elif
334 one line
335 1
336 2
337 sh syntax
338 1
339 2
340 ## END
341
342 #### parse_brace: brace group in if condition
343
344 # strict_errexit would make this a RUNTIME error
345 shopt -s parse_brace
346 if { echo one; echo two } {
347 echo three
348 }
349 ## STDOUT:
350 one
351 two
352 three
353 ## END
354
355 #### parse_brace: while/until
356 shopt -s oil:upgrade
357 while true {
358 echo one
359 break
360 }
361 while true { echo two; break }
362
363 echo 'sh syntax'
364 while true; do echo three; break; done
365 ## STDOUT:
366 one
367 two
368 sh syntax
369 three
370 ## END
371
372 #### parse_brace: for-in loop
373 shopt -s oil:upgrade
374 for x in one two {
375 echo $x
376 }
377 for x in three { echo $x }
378
379 echo 'sh syntax'
380 for x in four; do echo $x; done
381
382 ## STDOUT:
383 one
384 two
385 three
386 sh syntax
387 four
388 ## END
389
390 #### parse_brace case
391 shopt -s ysh:upgrade
392
393 var files = :| foo.py 'foo test.sh' |
394 for name in (files) {
395 case $name in
396 *.py)
397 echo python
398 ;;
399 *.sh)
400 echo shell
401 ;;
402 esac
403 }
404
405 for name in @files {
406 case (name) {
407 *.py {
408 echo python
409 }
410 *.sh { echo shell }
411 }
412 }
413
414 ## STDOUT:
415 python
416 shell
417 python
418 shell
419 ## END
420
421 #### parse_paren: if statement
422 shopt -s oil:upgrade
423 var x = 1
424 if (x < 42) {
425 echo less
426 }
427
428 if (x < 0) {
429 echo negative
430 } elif (x < 42) {
431 echo less
432 }
433
434 if (x < 0) {
435 echo negative
436 } elif (x < 1) {
437 echo less
438 } else {
439 echo other
440 }
441
442
443 ## STDOUT:
444 less
445 less
446 other
447 ## END
448
449 #### parse_paren: while statement
450 shopt -s oil:upgrade
451
452 # ksh style
453 var x = 1
454 while (( x < 3 )) {
455 echo $x
456 setvar x += 1
457 }
458 echo 'done ksh'
459
460 # sh style
461 var y = 1
462 while test $y -lt 3 {
463 echo $y
464 setvar y += 1
465 }
466 echo 'done sh'
467
468 # oil
469 var z = 1
470 while (z < 3) {
471 echo $z
472 setvar z += 1
473 }
474 echo 'done oil'
475
476 ## STDOUT:
477 1
478 2
479 done ksh
480 1
481 2
482 done sh
483 1
484 2
485 done oil
486 ## END
487
488 #### while subshell without parse_paren
489 while ( echo one ); do
490 echo two
491 break
492 done
493 ## STDOUT:
494 one
495 two
496 ## END
497
498 #### nullglob is on with oil:upgrade
499 write one *.zzz two
500 shopt -s oil:upgrade
501 write __
502 write one *.zzz two
503 ## STDOUT:
504 one
505 *.zzz
506 two
507 __
508 one
509 two
510 ## END
511
512 #### nullglob is on with oil:all
513 write one *.zzz two
514 shopt -s oil:all
515 write __
516 write one *.zzz two
517 ## STDOUT:
518 one
519 *.zzz
520 two
521 __
522 one
523 two
524 ## END
525
526 #### shopt -s simple_echo
527 foo='one two'
528 echo $foo # bad split then join
529 shopt -s simple_echo
530 echo
531 echo "$foo" # good
532 echo $foo
533
534 echo -e "$foo" # -e isn't special!
535 echo -n "$foo" # -n isn't special!
536
537 ## STDOUT:
538 one two
539
540 one two
541 one two
542 -e one two
543 -n one two
544 ## END
545
546 #### shopt -s dashglob
547 mkdir globdir
548 cd globdir
549
550 touch -- file -v
551
552 argv.py *
553
554 shopt -s oil:upgrade # turns OFF dashglob
555 argv.py *
556
557 shopt -s dashglob # turn it ON
558 argv.py *
559
560 ## STDOUT:
561 ['-v', 'file']
562 ['file']
563 ['-v', 'file']
564 ## END
565
566 #### shopt -s oil:upgrade turns some options on and others off
567 show() {
568 shopt -p | egrep 'dashglob|simple_word_eval'
569 }
570
571 show
572 echo ---
573
574 shopt -s simple_word_eval
575 show
576 echo ---
577
578 shopt -s oil:upgrade # strict_arith should still be on after this!
579 show
580 echo ---
581
582 shopt -u oil:upgrade # strict_arith should still be on after this!
583 show
584
585 ## STDOUT:
586 shopt -s dashglob
587 shopt -u simple_word_eval
588 ---
589 shopt -s dashglob
590 shopt -s simple_word_eval
591 ---
592 shopt -u dashglob
593 shopt -s simple_word_eval
594 ---
595 shopt -s dashglob
596 shopt -u simple_word_eval
597 ## END
598
599 #### sigpipe_status_ok
600
601 status_141() {
602 return 141
603 }
604
605 yes | head -n 1
606 echo ${PIPESTATUS[@]}
607
608 # DUMMY
609 yes | status_141
610 echo ${PIPESTATUS[@]}
611
612 shopt --set oil:upgrade # sigpipe_status_ok
613 shopt --unset errexit
614
615 yes | head -n 1
616 echo ${PIPESTATUS[@]}
617
618 # Conveniently, the last 141 isn't changed to 0, because it's run in the
619 # CURRENT process.
620
621 yes | status_141
622 echo ${PIPESTATUS[@]}
623
624 echo background
625 false | status_141 &
626 wait
627 echo status=$? pipestatus=${PIPESTATUS[@]}
628
629 ## STDOUT:
630 y
631 141 0
632 141 141
633 y
634 0 0
635 0 141
636 background
637 status=0 pipestatus=0 141
638 ## END
639
640
641 #### printf | head regression (sigpipe_status_ok)
642
643 shopt --set ysh:upgrade
644 shopt --unset errexit
645
646 bad() {
647 /usr/bin/printf '%65538s\n' foo | head -c 1
648 echo external on @_pipeline_status
649
650 shopt --unset sigpipe_status_ok {
651 /usr/bin/printf '%65538s\n' foo | head -c 1
652 }
653 echo external off @_pipeline_status
654
655 printf '%65538s\n' foo | head -c 1
656 echo builtin on @_pipeline_status
657
658 shopt --unset sigpipe_status_ok {
659 printf '%65538s\n' foo | head -c 1
660 }
661 echo builtin off @_pipeline_status
662 }
663
664 bad
665 echo finished
666
667 ## STDOUT:
668 external on 0 0
669 external off 141 0
670 builtin on 0 0
671 builtin off 141 0
672 finished
673 ## END
674
675 #### redefine_proc is on in interactive shell
676
677 $SH -O oil:all -i --rcfile /dev/null -c "
678 source $REPO_ROOT/spec/testdata/module/common.ysh
679 source $REPO_ROOT/spec/testdata/module/redefinition.ysh
680 log hi
681 "
682 ## STDOUT:
683 common
684 redefinition
685 ## END
686 ## STDERR:
687 hi
688 ## END
689
690
691 #### redefine_source is on in interactive shell
692
693 $SH -O oil:all -i --rcfile /dev/null -c "
694 source $REPO_ROOT/spec/testdata/module/common.ysh
695 source $REPO_ROOT/spec/testdata/module/common.ysh
696 log hi
697 " 2>stderr.txt
698 echo status=$?
699
700 # Make sure there are two lines
701 wc -l stderr.txt
702 ## STDOUT:
703 common
704 common
705 status=0
706 2 stderr.txt
707 ## END
708
709
710 #### parse options in sourced file (bug #1628)
711
712 set -e # catch errors
713
714 alias e=echo
715 shopt -u expand_aliases
716
717 source $REPO_ROOT/spec/testdata/parse_opts.sh a b c
718
719 echo OK
720
721 # alias persists
722 e alias on
723
724 # parse_paren doesn't persist
725 #if (x > 1) {
726 # echo 'OK'
727 #}
728
729 FOO=bar source $REPO_ROOT/spec/testdata/parse_opts.sh
730 echo OK
731
732
733 ## STDOUT:
734 OK
735 alias on
736 OK
737 ## END
738
739 #### expand_aliases turned off only in ysh:all
740
741 alias e=echo
742 e normal
743
744 shopt -s ysh:upgrade
745 e upgrade
746
747 shopt -s ysh:all
748 e all
749
750 ## status: 127
751 ## STDOUT:
752 normal
753 upgrade
754 ## END
755
756 #### [[ isn't allowed in ysh
757 [[ 3 == 3 ]]
758 echo status=$?
759
760 shopt -s ysh:upgrade
761 [[ 3 == 3 ]]
762 echo status=$?
763
764 shopt -s ysh:all
765 [[ 3 == 3 ]]
766 echo status=$?
767
768 [[ 0 == 0 ]]
769 echo status=$?
770
771 ## status: 2
772 ## STDOUT:
773 status=0
774 status=0
775 ## END
776
777 #### ysh:upgrade allows assoc arrays, with parse_ysh_string
778
779 shopt --set ysh:upgrade
780
781 # parse_ysh_string disallows it
782 # do we need another option? strict_parse_string or strict_parse_word?
783
784 declare -A assoc=(['key']=value ["dq"]=dq)
785
786 #declare -A assoc=([key]=value [dq]=dq)
787 echo ${#assoc[@]}
788
789 ## STDOUT:
790 2
791 ## END