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

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