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

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