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

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