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

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