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

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