OILS / spec / ysh-options.test.sh View on Github | oilshell.org

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