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