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