OILS / spec / ysh-options.test.sh View on Github | oils.pub

759 lines, 450 significant
1## oils_failures_allowed: 1
2
3# Test shell execution options.
4
5#### simple_word_eval doesn't split, glob, or elide empty
6mkdir mydir
7touch foo.z bar.z spam.z
8spaces='a b'
9dir=mydir
10glob=*.z
11prefix=sp
12set -- 'x y' z
13
14for i in 1 2; do
15 local empty=
16 argv.py $spaces $glob $empty $prefix*.z
17
18 # arrays still work too, with this weird rule
19 argv.py -"$@"-
20
21 shopt -s simple_word_eval
22done
23## STDOUT:
24['a', 'b', 'bar.z', 'foo.z', 'spam.z', 'spam.z']
25['-x y', 'z-']
26['a b', '*.z', '', 'spam.z']
27['-x y', 'z-']
28## END
29
30#### simple_word_eval and strict_array conflict over globs
31touch foo.txt bar.txt
32set -- f
33
34argv.py "$@"*.txt
35shopt -s simple_word_eval
36argv.py "$@"*.txt
37shopt -s strict_array
38argv.py "$@"*.txt
39
40## status: 1
41## STDOUT:
42['foo.txt']
43['foo.txt']
44## END
45
46#### simple_word_eval and glob
47shopt -s simple_word_eval
48
49# rm -v -f *.ff
50touch 1.ff 2.ff
51
52for i in *.ff; do
53 echo $i
54done
55
56array=(*.ff)
57echo "${array[@]}"
58
59echo *.ff
60
61## STDOUT:
621.ff
632.ff
641.ff 2.ff
651.ff 2.ff
66## END
67
68#### parse_at
69words=(a 'b c')
70argv.py @words
71
72shopt -s parse_at
73argv.py @words
74
75## STDOUT:
76['@words']
77['a', 'b c']
78## END
79
80#### DISABLED: parse_at can't be used outside top level
81
82# shopt -u expand_aliases conflicted with ble.sh, and it was also broken for
83# proc/func
84
85f() {
86 shopt -s parse_at
87 echo status=$?
88}
89f
90echo 'should not get here'
91## status: 1
92## stdout-json: ""
93
94
95#### sourcing a file that sets parse_at
96cat >lib.sh <<EOF
97shopt -s parse_at
98echo lib.sh
99EOF
100
101words=(a 'b c')
102argv.py @words
103
104# This has a side effect, which is a bit weird, but not sure how to avoid it.
105# Maybe we should say that libraries aren't allowed to change it?
106
107source lib.sh
108echo 'main.sh'
109
110argv.py @words
111## STDOUT:
112['@words']
113lib.sh
114main.sh
115['a', 'b c']
116## END
117
118#### parse_at can be specified through sh -O
119$SH +O parse_at -c 'words=(a "b c"); argv.py @words'
120$SH -O parse_at -c 'words=(a "b c"); argv.py @words'
121## STDOUT:
122['@words']
123['a', 'b c']
124## END
125
126#### @a splices into $0
127shopt -s simple_word_eval parse_at
128a=(echo hi)
129"${a[@]}"
130@a
131
132# Bug fix
133shopt -s strict_array
134
135"${a[@]}"
136@a
137## STDOUT:
138hi
139hi
140hi
141hi
142## END
143
144#### shopt -s strict:all
145shopt -s strict:all
146# normal option names
147shopt -o -p | grep -- ' -o ' | grep -v hashall
148shopt -p strict:all
149## STDOUT:
150shopt -s strict_arg_parse
151shopt -s strict_argv
152shopt -s strict_arith
153shopt -s strict_array
154shopt -s strict_control_flow
155shopt -s strict_env_binding
156shopt -s strict_errexit
157shopt -s strict_glob
158shopt -s strict_nameref
159shopt -s strict_parse_equals
160shopt -s strict_parse_slice
161shopt -s strict_tilde
162shopt -s strict_word_eval
163## END
164
165#### shopt -s ysh:upgrade
166shopt -s ysh:upgrade
167# normal option names
168shopt -o -p | grep -- ' -o ' | grep -v hashall
169shopt -p ysh:upgrade
170## STDOUT:
171set -o errexit
172set -o nounset
173set -o pipefail
174shopt -s command_sub_errexit
175shopt -s env_obj
176shopt -s errexit
177shopt -s for_loop_frames
178shopt -s inherit_errexit
179shopt -s init_ysh_globals
180shopt -s no_dash_glob
181shopt -s no_xtrace_osh
182shopt -s nounset
183shopt -s nullglob
184shopt -s parse_at
185shopt -s parse_brace
186shopt -s parse_bracket
187shopt -s parse_equals
188shopt -s parse_func
189shopt -s parse_paren
190shopt -s parse_proc
191shopt -s parse_triple_quote
192shopt -s parse_ysh_string
193shopt -s pipefail
194shopt -s process_sub_fail
195shopt -s sigpipe_status_ok
196shopt -s simple_word_eval
197shopt -s verbose_errexit
198shopt -s verbose_warn
199shopt -s xtrace_rich
200## END
201
202#### osh -O ysh:upgrade
203$SH -O ysh:upgrade -c 'var x = :|one two three|; write @x'
204## STDOUT:
205one
206two
207three
208## END
209
210#### osh -O errexit: use -O everywhere, even for Bourne options
211$SH -O errexit -c 'shopt -p -o errexit'
212#$SH -O errexit -c 'shopt -p errexit' # bash doesn't allow this, but Oils does
213## STDOUT:
214set -o errexit
215## END
216
217#### osh -O invalid
218$SH -O errexit -c 'echo hi'
219echo status=$?
220$SH -O invalid -c 'echo hi'
221echo status=$?
222## STDOUT:
223hi
224status=0
225status=2
226## END
227
228#### osh -o new_option is also accepted
229
230$SH -o nullglob -c 'echo nullglob'
231echo $? flag nullglob
232
233$SH -o oil:upgrade -c 'proc p { echo upgrade }; p'
234echo $? flag oil:upgrade
235
236# Should disallow these
237
238set -o nullglob
239echo $? set builtin nullglob
240set -o oil:upgrade
241echo $? set builtin oil:upgrade
242
243## STDOUT:
244nullglob
2450 flag nullglob
246upgrade
2470 flag oil:upgrade
2482 set builtin nullglob
2492 set builtin oil:upgrade
250## END
251
252
253#### oil:upgrade includes inherit_errexit
254shopt -s oil:upgrade
255echo $(echo one; false; echo two)
256## status: 1
257## stdout-json: ""
258
259#### parse_brace: bad block to assignment builtin
260shopt -s oil:upgrade
261# This is a fatal programming error. It's unlike passing an extra arg?
262local x=y { echo 'bad block' }
263echo status=$?
264## status: 1
265## stdout-json: ""
266
267#### parse_brace: bad block to external program
268shopt -s oil:upgrade
269# This is a fatal programming error. It's unlike passing an extra arg?
270ls { echo 'bad block' }
271echo status=$?
272## status: 1
273## stdout-json: ""
274
275#### parse_brace: cd { } in pipeline
276shopt -s oil:upgrade
277cd /tmp {
278 pwd
279 pwd
280} | tr a-z A-Z
281## STDOUT:
282/TMP
283/TMP
284## END
285
286
287#### parse_brace: if accepts blocks
288shopt -s oil:upgrade
289shopt -u errexit # don't need strict_errexit check!
290
291if test -n foo {
292 echo one
293}
294# harder
295if test -n foo; test -n bar {
296 echo two
297}
298
299# just like POSIX shell!
300if test -n foo;
301
302 test -n bar {
303 echo three
304}
305
306if test -z foo {
307 echo if
308} else {
309 echo else
310}
311
312if test -z foo {
313 echo if
314} elif test -z '' {
315 echo elif
316} else {
317 echo else
318}
319
320echo 'one line'
321if test -z foo { echo if } elif test -z '' { echo 1 }; if test -n foo { echo 2 };
322
323echo 'sh syntax'
324if test -z foo; then echo if; elif test -z ''; then echo 1; fi; if test -n foo { echo 2 };
325
326# NOTE: This is not allowed because it's like a brace group!
327# if test -n foo; {
328
329## STDOUT:
330one
331two
332three
333else
334elif
335one line
3361
3372
338sh syntax
3391
3402
341## END
342
343#### parse_brace: brace group in if condition
344
345# strict_errexit would make this a RUNTIME error
346shopt -s parse_brace
347if { echo one; echo two } {
348 echo three
349}
350## STDOUT:
351one
352two
353three
354## END
355
356#### parse_brace: while/until
357shopt -s oil:upgrade
358while true {
359 echo one
360 break
361}
362while true { echo two; break }
363
364echo 'sh syntax'
365while true; do echo three; break; done
366## STDOUT:
367one
368two
369sh syntax
370three
371## END
372
373#### parse_brace: for-in loop
374shopt -s oil:upgrade
375for x in one two {
376 echo $x
377}
378for x in three { echo $x }
379
380echo 'sh syntax'
381for x in four; do echo $x; done
382
383## STDOUT:
384one
385two
386three
387sh syntax
388four
389## END
390
391#### parse_brace case
392shopt -s ysh:upgrade
393
394var files = :| foo.py 'foo test.sh' |
395for name in (files) {
396 case $name in
397 *.py)
398 echo python
399 ;;
400 *.sh)
401 echo shell
402 ;;
403 esac
404}
405
406for name in @files {
407 case (name) {
408 *.py {
409 echo python
410 }
411 *.sh { echo shell }
412 }
413}
414
415## STDOUT:
416python
417shell
418python
419shell
420## END
421
422#### parse_paren: if statement
423shopt -s oil:upgrade
424var x = 1
425if (x < 42) {
426 echo less
427}
428
429if (x < 0) {
430 echo negative
431} elif (x < 42) {
432 echo less
433}
434
435if (x < 0) {
436 echo negative
437} elif (x < 1) {
438 echo less
439} else {
440 echo other
441}
442
443
444## STDOUT:
445less
446less
447other
448## END
449
450#### parse_paren: while statement
451shopt -s oil:upgrade
452
453# ksh style
454var x = 1
455while (( x < 3 )) {
456 echo $x
457 setvar x += 1
458}
459echo 'done ksh'
460
461# sh style
462var y = 1
463while test $y -lt 3 {
464 echo $y
465 setvar y += 1
466}
467echo 'done sh'
468
469# oil
470var z = 1
471while (z < 3) {
472 echo $z
473 setvar z += 1
474}
475echo 'done oil'
476
477## STDOUT:
4781
4792
480done ksh
4811
4822
483done sh
4841
4852
486done oil
487## END
488
489#### while subshell without parse_paren
490while ( echo one ); do
491 echo two
492 break
493done
494## STDOUT:
495one
496two
497## END
498
499#### nullglob is on with oil:upgrade
500write one *.zzz two
501shopt -s oil:upgrade
502write __
503write one *.zzz two
504## STDOUT:
505one
506*.zzz
507two
508__
509one
510two
511## END
512
513#### nullglob is on with oil:all
514write one *.zzz two
515shopt -s oil:all
516write __
517write one *.zzz two
518## STDOUT:
519one
520*.zzz
521two
522__
523one
524two
525## END
526
527#### shopt -s simple_echo
528foo='one two'
529echo $foo # bad split then join
530shopt -s simple_echo
531echo
532echo "$foo" # good
533echo $foo
534
535echo -e "$foo" # -e isn't special!
536echo -n "$foo" # -n isn't special!
537
538## STDOUT:
539one two
540
541one two
542one two
543-e one two
544-n one two
545## END
546
547#### shopt -s no_dash_glob
548mkdir globdir
549cd globdir
550
551touch -- file -v
552
553argv.py *
554
555shopt -s ysh:upgrade # turns OFF no_dash_glob
556argv.py *
557
558shopt -u no_dash_glob # turn it ON
559argv.py *
560
561## STDOUT:
562['-v', 'file']
563['file']
564['-v', 'file']
565## END
566
567#### sigpipe_status_ok
568
569status_141() {
570 return 141
571}
572
573yes | head -n 1
574echo ${PIPESTATUS[@]}
575
576# DUMMY
577yes | status_141
578echo ${PIPESTATUS[@]}
579
580shopt --set oil:upgrade # sigpipe_status_ok
581shopt --unset errexit
582
583yes | head -n 1
584echo ${PIPESTATUS[@]}
585
586# Conveniently, the last 141 isn't changed to 0, because it's run in the
587# CURRENT process.
588
589yes | status_141
590echo ${PIPESTATUS[@]}
591
592echo background
593false | status_141 &
594wait
595echo status=$? pipestatus=${PIPESTATUS[@]}
596
597## STDOUT:
598y
599141 0
600141 141
601y
6020 0
6030 141
604background
605status=0 pipestatus=0 141
606## END
607
608
609#### printf | head regression (sigpipe_status_ok)
610
611shopt --set ysh:upgrade
612shopt --unset errexit
613
614bad() {
615 /usr/bin/printf '%65538s\n' foo | head -c 1
616 echo external on @_pipeline_status
617
618 shopt --unset sigpipe_status_ok {
619 /usr/bin/printf '%65538s\n' foo | head -c 1
620 }
621 echo external off @_pipeline_status
622
623 printf '%65538s\n' foo | head -c 1
624 echo builtin on @_pipeline_status
625
626 shopt --unset sigpipe_status_ok {
627 printf '%65538s\n' foo | head -c 1
628 }
629 echo builtin off @_pipeline_status
630}
631
632bad
633echo finished
634
635## STDOUT:
636 external on 0 0
637 external off 141 0
638 builtin on 0 0
639 builtin off 141 0
640finished
641## END
642
643#### redefine_proc is on in interactive shell
644
645$SH -O oil:all -i --rcfile /dev/null -c "
646source $REPO_ROOT/spec/testdata/module/common.ysh
647source $REPO_ROOT/spec/testdata/module/redefinition.ysh
648log hi
649"
650## STDOUT:
651common
652redefinition
653## END
654## STDERR:
655hi
656## END
657
658
659#### redefine_source is on in interactive shell
660
661$SH -O oil:all -i --rcfile /dev/null -c "
662source $REPO_ROOT/spec/testdata/module/common.ysh
663source $REPO_ROOT/spec/testdata/module/common.ysh
664log hi
665" 2>stderr.txt
666echo status=$?
667
668# Make sure there are two lines
669wc -l stderr.txt
670## STDOUT:
671common
672common
673status=0
6742 stderr.txt
675## END
676
677
678#### parse options in sourced file (bug #1628)
679
680set -e # catch errors
681
682alias e=echo
683shopt -u expand_aliases
684
685source $REPO_ROOT/spec/testdata/parse_opts.sh a b c
686
687echo OK
688
689# alias persists
690e alias on
691
692# parse_paren doesn't persist
693#if (x > 1) {
694# echo 'OK'
695#}
696
697FOO=bar source $REPO_ROOT/spec/testdata/parse_opts.sh
698echo OK
699
700
701## STDOUT:
702OK
703alias on
704OK
705## END
706
707#### expand_aliases turned off only in ysh:all
708
709alias e=echo
710e normal
711
712shopt -s ysh:upgrade
713e upgrade
714
715shopt -s ysh:all
716e all
717
718## status: 127
719## STDOUT:
720normal
721upgrade
722## END
723
724#### [[ isn't allowed in ysh
725[[ 3 == 3 ]]
726echo status=$?
727
728shopt -s ysh:upgrade
729[[ 3 == 3 ]]
730echo status=$?
731
732shopt -s ysh:all
733[[ 3 == 3 ]]
734echo status=$?
735
736[[ 0 == 0 ]]
737echo status=$?
738
739## status: 2
740## STDOUT:
741status=0
742status=0
743## END
744
745#### ysh:upgrade allows assoc arrays, with parse_ysh_string
746
747shopt --set ysh:upgrade
748
749# parse_ysh_string disallows it
750# do we need another option? strict_parse_string or strict_parse_word?
751
752declare -A assoc=(['key']=value ["dq"]=dq)
753
754#declare -A assoc=([key]=value [dq]=dq)
755echo ${#assoc[@]}
756
757## STDOUT:
7582
759## END