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