OILS / spec / builtin-read.test.sh View on Github | oilshell.org

770 lines, 344 significant
1## oils_failures_allowed: 2
2## compare_shells: bash mksh zsh ash
3
4#### read line from here doc
5
6# NOTE: there are TABS below
7read x <<EOF
8A B C D E
9FG
10EOF
11echo "[$x]"
12## stdout: [A B C D E]
13## status: 0
14
15#### read from empty file
16echo -n '' > $TMP/empty.txt
17read x < $TMP/empty.txt
18argv.py "status=$?" "$x"
19
20# No variable name, behaves the same
21read < $TMP/empty.txt
22argv.py "status=$?" "$REPLY"
23
24## STDOUT:
25['status=1', '']
26['status=1', '']
27## END
28## OK dash STDOUT:
29['status=1', '']
30['status=2', '']
31## END
32## status: 0
33
34#### read /dev/null
35read -n 1 </dev/null
36echo $?
37## STDOUT:
381
39## END
40## OK dash stdout: 2
41
42#### read with zero args
43echo | read
44echo status=$?
45## STDOUT:
46status=0
47## END
48## BUG dash STDOUT:
49status=2
50## END
51
52#### read builtin with no newline returns status 1
53
54# This is odd because the variable is populated successfully. OSH/YSH might
55# need a separate put reading feature that doesn't use IFS.
56
57echo -n ZZZ | { read x; echo status=$?; echo $x; }
58
59## STDOUT:
60status=1
61ZZZ
62## END
63## status: 0
64
65#### read builtin splits value across multiple vars
66# NOTE: there are TABS below
67read x y z <<EOF
68A B C D E
69FG
70EOF
71echo "[$x/$y/$z]"
72## stdout: [A/B/C D E]
73## status: 0
74
75#### read builtin with too few variables
76set -o errexit
77set -o nounset # hm this doesn't change it
78read x y z <<EOF
79A B
80EOF
81echo /$x/$y/$z/
82## stdout: /A/B//
83## status: 0
84
85#### read -n (with $REPLY)
86echo 12345 > $TMP/readn.txt
87read -n 4 x < $TMP/readn.txt
88read -n 2 < $TMP/readn.txt # Do it again with no variable
89argv.py $x $REPLY
90## stdout: ['1234', '12']
91## N-I dash/zsh stdout: []
92
93#### IFS= read -n (OSH regression: value saved in tempenv)
94echo XYZ > "$TMP/readn.txt"
95IFS= TMOUT= read -n 1 char < "$TMP/readn.txt"
96argv.py "$char"
97## stdout: ['X']
98## N-I dash/zsh stdout: ['']
99
100#### read -n doesn't strip whitespace (bug fix)
101case $SH in dash|zsh) exit ;; esac
102
103echo ' a b ' | (read -n 4; echo "[$REPLY]")
104echo ' a b ' | (read -n 5; echo "[$REPLY]")
105echo ' a b ' | (read -n 6; echo "[$REPLY]")
106echo
107
108echo 'one var strips whitespace'
109echo ' a b ' | (read -n 4 myvar; echo "[$myvar]")
110echo ' a b ' | (read -n 5 myvar; echo "[$myvar]")
111echo ' a b ' | (read -n 6 myvar; echo "[$myvar]")
112echo
113
114echo 'three vars'
115echo ' a b ' | (read -n 4 x y z; echo "[$x] [$y] [$z]")
116echo ' a b ' | (read -n 5 x y z; echo "[$x] [$y] [$z]")
117echo ' a b ' | (read -n 6 x y z; echo "[$x] [$y] [$z]")
118
119## STDOUT:
120[ a ]
121[ a b]
122[ a b ]
123
124one var strips whitespace
125[a]
126[a b]
127[a b]
128
129three vars
130[a] [] []
131[a] [b] []
132[a] [b] []
133## END
134
135## N-I dash/zsh STDOUT:
136## END
137
138## BUG mksh STDOUT:
139[a]
140[a b]
141[a b]
142
143one var strips whitespace
144[a]
145[a b]
146[a b]
147
148three vars
149[a] [] []
150[a] [b] []
151[a] [b] []
152## END
153
154#### read -d -n - respects delimiter and splits
155
156case $SH in dash|zsh|ash) exit ;; esac
157
158echo 'delim c'
159echo ' a b c ' | (read -d 'c' -n 3; echo "[$REPLY]")
160echo ' a b c ' | (read -d 'c' -n 4; echo "[$REPLY]")
161echo ' a b c ' | (read -d 'c' -n 5; echo "[$REPLY]")
162echo
163
164echo 'one var'
165echo ' a b c ' | (read -d 'c' -n 3 myvar; echo "[$myvar]")
166echo ' a b c ' | (read -d 'c' -n 4 myvar; echo "[$myvar]")
167echo ' a b c ' | (read -d 'c' -n 5 myvar; echo "[$myvar]")
168echo
169
170echo 'three vars'
171echo ' a b c ' | (read -d 'c' -n 3 x y z; echo "[$x] [$y] [$z]")
172echo ' a b c ' | (read -d 'c' -n 4 x y z; echo "[$x] [$y] [$z]")
173echo ' a b c ' | (read -d 'c' -n 5 x y z; echo "[$x] [$y] [$z]")
174
175## STDOUT:
176delim c
177[ a]
178[ a ]
179[ a b]
180
181one var
182[a]
183[a]
184[a b]
185
186three vars
187[a] [] []
188[a] [] []
189[a] [b] []
190## END
191
192## N-I dash/zsh/ash STDOUT:
193## END
194
195## BUG mksh STDOUT:
196delim c
197[a]
198[a]
199[a b]
200
201one var
202[a]
203[a]
204[a b]
205
206three vars
207[a] [] []
208[a] [] []
209[a] [b] []
210## END
211
212
213#### read -n with invalid arg
214read -n not_a_number
215echo status=$?
216## stdout: status=2
217## OK bash stdout: status=1
218## N-I zsh stdout-json: ""
219
220#### read -n from pipe
221case $SH in (dash|ash|zsh) exit ;; esac
222
223echo abcxyz | { read -n 3; echo reply=$REPLY; }
224## status: 0
225## stdout: reply=abc
226## N-I dash/ash/zsh stdout-json: ""
227
228# zsh appears to hang with -k
229## N-I zsh stdout-json: ""
230
231#### read without args uses $REPLY, no splitting occurs (without -n)
232
233# mksh and zsh implement splitting with $REPLY, bash/ash don't
234
235echo ' a b ' | (read; echo "[$REPLY]")
236echo ' a b ' | (read myvar; echo "[$myvar]")
237
238echo ' a b \
239 line2' | (read; echo "[$REPLY]")
240echo ' a b \
241 line2' | (read myvar; echo "[$myvar]")
242
243# Now test with -r
244echo ' a b \
245 line2' | (read -r; echo "[$REPLY]")
246echo ' a b \
247 line2' | (read -r myvar; echo "[$myvar]")
248
249## STDOUT:
250[ a b ]
251[a b]
252[ a b line2]
253[a b line2]
254[ a b \]
255[a b \]
256## END
257## N-I dash stdout:
258## BUG mksh/zsh STDOUT:
259[a b]
260[a b]
261[a b line2]
262[a b line2]
263[a b \]
264[a b \]
265## END
266## BUG dash STDOUT:
267[]
268[a b ]
269[]
270[a b line2]
271[]
272[a b \]
273## END
274
275#### read -n vs. -N
276# dash, ash and zsh do not implement read -N
277# mksh treats -N exactly the same as -n
278case $SH in (dash|ash|zsh) exit ;; esac
279
280# bash docs: https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html
281
282echo 'a b c' > $TMP/readn.txt
283
284echo 'read -n'
285read -n 5 A B C < $TMP/readn.txt; echo "'$A' '$B' '$C'"
286read -n 4 A B C < $TMP/readn.txt; echo "'$A' '$B' '$C'"
287echo
288
289echo 'read -N'
290read -N 5 A B C < $TMP/readn.txt; echo "'$A' '$B' '$C'"
291read -N 4 A B C < $TMP/readn.txt; echo "'$A' '$B' '$C'"
292## STDOUT:
293read -n
294'a' 'b' 'c'
295'a' 'b' ''
296
297read -N
298'a b c' '' ''
299'a b ' '' ''
300## END
301## N-I dash/ash/zsh stdout-json: ""
302## BUG mksh STDOUT:
303read -n
304'a' 'b' 'c'
305'a' 'b' ''
306
307read -N
308'a' 'b' 'c'
309'a' 'b' ''
310## END
311
312#### read -N ignores delimiters
313case $SH in (dash|ash|zsh) exit ;; esac
314
315echo $'a\nb\nc' > $TMP/read-lines.txt
316
317read -N 3 out < $TMP/read-lines.txt
318echo "$out"
319## STDOUT:
320a
321b
322## END
323## N-I dash/ash/zsh stdout-json: ""
324
325#### read will unset extranous vars
326
327echo 'a b' > $TMP/read-few.txt
328
329c='some value'
330read a b c < $TMP/read-few.txt
331echo "'$a' '$b' '$c'"
332
333case $SH in (dash) exit ;; esac # dash does not implement -n
334
335c='some value'
336read -n 3 a b c < $TMP/read-few.txt
337echo "'$a' '$b' '$c'"
338## STDOUT:
339'a' 'b' ''
340'a' 'b' ''
341## END
342## N-I dash STDOUT:
343'a' 'b' ''
344## END
345## BUG zsh STDOUT:
346'a' 'b' ''
347'b' '' ''
348## END
349
350#### read -r ignores backslashes
351echo 'one\ two' > $TMP/readr.txt
352read escaped < $TMP/readr.txt
353read -r raw < $TMP/readr.txt
354argv.py "$escaped" "$raw"
355## stdout: ['one two', 'one\\ two']
356
357#### read -r with other backslash escapes
358echo 'one\ two\x65three' > $TMP/readr.txt
359read escaped < $TMP/readr.txt
360read -r raw < $TMP/readr.txt
361argv.py "$escaped" "$raw"
362# mksh respects the hex escapes here, but other shells don't!
363## stdout: ['one twox65three', 'one\\ two\\x65three']
364## BUG mksh/zsh stdout: ['one twoethree', 'one\\ twoethree']
365
366#### read with line continuation reads multiple physical lines
367# NOTE: osh failing because of file descriptor issue. stdin has to be closed!
368tmp=$TMP/$(basename $SH)-readr.txt
369echo -e 'one\\\ntwo\n' > $tmp
370read escaped < $tmp
371read -r raw < $tmp
372argv.py "$escaped" "$raw"
373## stdout: ['onetwo', 'one\\']
374## N-I dash stdout: ['-e onetwo', '-e one\\']
375
376#### read multiple vars spanning many lines
377read x y << 'EOF'
378one-\
379two three-\
380four five-\
381six
382EOF
383argv.py "$x" "$y" "$z"
384## stdout: ['one-two', 'three-four five-six', '']
385
386#### read -r with \n
387echo '\nline' > $TMP/readr.txt
388read escaped < $TMP/readr.txt
389read -r raw < $TMP/readr.txt
390argv.py "$escaped" "$raw"
391# dash/mksh/zsh are bugs because at least the raw mode should let you read a
392# literal \n.
393## stdout: ['nline', '\\nline']
394## BUG dash/mksh/zsh stdout: ['', '']
395
396#### read -s from pipe, not a terminal
397case $SH in (dash|zsh) exit ;; esac
398
399# It's hard to really test this because it requires a terminal. We hit a
400# different code path when reading through a pipe. There can be bugs there
401# too!
402
403echo foo | { read -s; echo $REPLY; }
404echo bar | { read -n 2 -s; echo $REPLY; }
405
406# Hm no exit 1 here? Weird
407echo b | { read -n 2 -s; echo $?; echo $REPLY; }
408## STDOUT:
409foo
410ba
4110
412b
413## END
414## N-I dash/zsh stdout-json: ""
415
416#### read with IFS=$'\n'
417# The leading spaces are stripped if they appear in IFS.
418IFS=$(echo -e '\n')
419read var <<EOF
420 a b c
421 d e f
422EOF
423echo "[$var]"
424## stdout: [ a b c]
425## N-I dash stdout: [a b c]
426
427#### read multiple lines with IFS=:
428# The leading spaces are stripped if they appear in IFS.
429# IFS chars are escaped with :.
430tmp=$TMP/$(basename $SH)-read-ifs.txt
431IFS=:
432cat >$tmp <<'EOF'
433 \\a :b\: c:d\
434 e
435EOF
436read a b c d < $tmp
437# Use printf because echo in dash/mksh interprets escapes, while it doesn't in
438# bash.
439printf "%s\n" "[$a|$b|$c|$d]"
440## stdout: [ \a |b: c|d e|]
441
442#### read with IFS=''
443IFS=''
444read x y <<EOF
445 a b c d
446EOF
447echo "[$x|$y]"
448## stdout: [ a b c d|]
449
450#### read does not respect C backslash escapes
451
452# bash doesn't respect these, but other shells do. Gah! I think bash
453# behavior makes more sense. It only escapes IFS.
454echo '\a \b \c \d \e \f \g \h \x65 \145 \i' > $TMP/read-c.txt
455read line < $TMP/read-c.txt
456echo $line
457## stdout-json: "a b c d e f g h x65 145 i\n"
458## BUG ash stdout-json: "abcdefghx65 145 i\n"
459## BUG dash/zsh stdout-json: "\u0007 \u0008\n"
460## BUG mksh stdout-json: "\u0007 \u0008 d \u001b \u000c g h e 145 i\n"
461
462#### dynamic scope used to set vars
463f() {
464 read head << EOF
465ref: refs/heads/dev/andy
466EOF
467}
468f
469echo $head
470## STDOUT:
471ref: refs/heads/dev/andy
472## END
473
474#### read -a reads into array
475
476# read -a is used in bash-completion
477# none of these shells implement it
478case $SH in
479 *mksh|*dash|*zsh|*/ash)
480 exit 2;
481 ;;
482esac
483
484read -a myarray <<'EOF'
485a b c\ d
486EOF
487argv.py "${myarray[@]}"
488
489# arguments are ignored here
490read -r -a array2 extra arguments <<'EOF'
491a b c\ d
492EOF
493argv.py "${array2[@]}"
494argv.py "${extra[@]}"
495argv.py "${arguments[@]}"
496## status: 0
497## STDOUT:
498['a', 'b', 'c d']
499['a', 'b', 'c\\', 'd']
500[]
501[]
502## END
503## N-I dash/mksh/zsh/ash status: 2
504## N-I dash/mksh/zsh/ash stdout-json: ""
505
506#### read -d : (colon-separated records)
507printf a,b,c:d,e,f:g,h,i | {
508 IFS=,
509 read -d : v1
510 echo "v1=$v1"
511 read -d : v1 v2
512 echo "v1=$v1 v2=$v2"
513 read -d : v1 v2 v3
514 echo "v1=$v1 v2=$v2 v3=$v3"
515}
516## STDOUT:
517v1=a,b,c
518v1=d v2=e,f
519v1=g v2=h v3=i
520## END
521## N-I dash STDOUT:
522v1=
523v1= v2=
524v1= v2= v3=
525## END
526
527#### read -d '' (null-separated records)
528printf 'a,b,c\0d,e,f\0g,h,i' | {
529 IFS=,
530 read -d '' v1
531 echo "v1=$v1"
532 read -d '' v1 v2
533 echo "v1=$v1 v2=$v2"
534 read -d '' v1 v2 v3
535 echo "v1=$v1 v2=$v2 v3=$v3"
536}
537## STDOUT:
538v1=a,b,c
539v1=d v2=e,f
540v1=g v2=h v3=i
541## END
542## N-I dash STDOUT:
543v1=
544v1= v2=
545v1= v2= v3=
546## END
547
548#### read -rd
549read -rd '' var <<EOF
550foo
551bar
552EOF
553echo "$var"
554## STDOUT:
555foo
556bar
557## END
558## N-I dash stdout-json: "\n"
559
560#### read -d when there's no delimiter
561{ read -d : part
562 echo $part $?
563 read -d : part
564 echo $part $?
565} <<EOF
566foo:bar
567EOF
568## STDOUT:
569foo 0
570bar 1
571## END
572## N-I dash STDOUT:
5732
5742
575## END
576
577#### read -t 0 tests if input is available
578case $SH in (dash|zsh|mksh) exit ;; esac
579
580# is there input available?
581read -t 0 < /dev/null
582echo $?
583
584# floating point
585read -t 0.0 < /dev/null
586echo $?
587
588# floating point
589echo foo | { read -t 0; echo reply=$REPLY; }
590echo $?
591
592## STDOUT:
5930
5940
595reply=
5960
597## END
598## N-I dash/zsh/mksh stdout-json: ""
599
600#### read -t 0.5
601case $SH in (dash) exit ;; esac
602
603read -t 0.5 < /dev/null
604echo $?
605
606## STDOUT:
6071
608## END
609## BUG zsh/mksh STDOUT:
6101
611## END
612## N-I dash stdout-json: ""
613
614#### read -t -0.5 is invalid
615# bash appears to just take the absolute value?
616
617read -t -0.5 < /dev/null
618echo $?
619
620## STDOUT:
6212
622## END
623## BUG bash STDOUT:
6241
625## END
626## BUG zsh stdout-json: ""
627## BUG zsh status: 1
628
629#### read -u
630case $SH in (dash|mksh) exit ;; esac
631
632# file descriptor
633read -u 3 3<<EOF
634hi
635EOF
636echo reply=$REPLY
637## STDOUT:
638reply=hi
639## END
640## N-I dash/mksh stdout-json: ""
641
642#### read -u syntax error
643read -u -3
644echo status=$?
645## STDOUT:
646status=2
647## END
648## OK bash/zsh STDOUT:
649status=1
650## END
651
652#### read -N doesn't respect delimiter, while read -n does
653case $SH in (dash|zsh|ash) exit ;; esac
654
655echo foobar | { read -n 5 -d b; echo $REPLY; }
656echo foobar | { read -N 5 -d b; echo $REPLY; }
657## STDOUT:
658foo
659fooba
660## END
661## OK mksh STDOUT:
662fooba
663fooba
664## END
665## N-I dash/zsh/ash stdout-json: ""
666
667#### read -p (not fully tested)
668
669# hm DISABLED if we're not going to the terminal
670# so we're only testing that it accepts the flag here
671
672case $SH in (dash|mksh|zsh) exit ;; esac
673
674echo hi | { read -p 'P'; echo $REPLY; }
675echo hi | { read -p 'P' -n 1; echo $REPLY; }
676## STDOUT:
677hi
678h
679## END
680## stderr-json: ""
681## N-I dash/mksh/zsh stdout-json: ""
682
683#### read usage
684read -n -1
685echo status=$?
686## STDOUT:
687status=2
688## END
689## OK bash stdout: status=1
690## BUG mksh stdout-json: ""
691# zsh gives a fatal error? seems inconsistent
692## BUG zsh stdout-json: ""
693## BUG zsh status: 1
694
695#### read with smooshed args
696echo hi | { read -rn1 var; echo var=$var; }
697## STDOUT:
698var=h
699## END
700## N-I dash/zsh STDOUT:
701var=
702## END
703
704#### read -r -d '' for NUL strings, e.g. find -print0
705
706
707case $SH in (dash|zsh|mksh) exit ;; esac # NOT IMPLEMENTED
708
709mkdir -p read0
710cd read0
711rm -f *
712
713touch a\\b\\c\\d # -r is necessary!
714
715find . -type f -a -print0 | { read -r -d ''; echo "[$REPLY]"; }
716
717## STDOUT:
718[./a\b\c\d]
719## END
720## N-I dash/zsh/mksh STDOUT:
721## END
722
723
724#### read from redirected directory is non-fatal error
725
726# This tickles an infinite loop bug in our version of mksh! TODO: upgrade the
727# version and enable this
728case $SH in (mksh) return ;; esac
729
730cd $TMP
731mkdir -p dir
732read x < ./dir
733echo status=$?
734
735## STDOUT:
736status=1
737## END
738# OK mksh stdout: status=2
739## OK mksh stdout-json: ""
740
741#### read -n from directory
742
743case $SH in (dash|ash) return ;; esac # not implemented
744
745# same hanging bug
746case $SH in (mksh) return ;; esac
747
748mkdir -p dir
749read -n 3 x < ./dir
750echo status=$?
751## STDOUT:
752status=1
753## END
754## OK mksh stdout-json: ""
755## N-I dash/ash stdout-json: ""
756
757#### mapfile from directory (bash doesn't handle errors)
758case $SH in (dash|ash|mksh|zsh) return ;; esac # not implemented
759
760mkdir -p dir
761mapfile $x < ./dir
762echo status=$?
763
764## STDOUT:
765status=1
766## END
767## BUG bash STDOUT:
768status=0
769## END
770## N-I dash/ash/mksh/zsh stdout-json: ""