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

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