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