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

769 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 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## BUG mksh/zsh STDOUT:
258[a b]
259[a b]
260[a b line2]
261[a b line2]
262[a b \]
263[a b \]
264## END
265## BUG dash STDOUT:
266[]
267[a b ]
268[]
269[a b line2]
270[]
271[a b \]
272## END
273
274#### read -n vs. -N
275# dash, ash and zsh do not implement read -N
276# mksh treats -N exactly the same as -n
277case $SH in (dash|ash|zsh) exit ;; esac
278
279# bash docs: https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html
280
281echo 'a b c' > $TMP/readn.txt
282
283echo 'read -n'
284read -n 5 A B C < $TMP/readn.txt; echo "'$A' '$B' '$C'"
285read -n 4 A B C < $TMP/readn.txt; echo "'$A' '$B' '$C'"
286echo
287
288echo 'read -N'
289read -N 5 A B C < $TMP/readn.txt; echo "'$A' '$B' '$C'"
290read -N 4 A B C < $TMP/readn.txt; echo "'$A' '$B' '$C'"
291## STDOUT:
292read -n
293'a' 'b' 'c'
294'a' 'b' ''
295
296read -N
297'a b c' '' ''
298'a b ' '' ''
299## END
300## N-I dash/ash/zsh stdout-json: ""
301## BUG mksh STDOUT:
302read -n
303'a' 'b' 'c'
304'a' 'b' ''
305
306read -N
307'a' 'b' 'c'
308'a' 'b' ''
309## END
310
311#### read -N ignores delimiters
312case $SH in (dash|ash|zsh) exit ;; esac
313
314echo $'a\nb\nc' > $TMP/read-lines.txt
315
316read -N 3 out < $TMP/read-lines.txt
317echo "$out"
318## STDOUT:
319a
320b
321## END
322## N-I dash/ash/zsh stdout-json: ""
323
324#### read will unset extranous vars
325
326echo 'a b' > $TMP/read-few.txt
327
328c='some value'
329read a b c < $TMP/read-few.txt
330echo "'$a' '$b' '$c'"
331
332case $SH in (dash) exit ;; esac # dash does not implement -n
333
334c='some value'
335read -n 3 a b c < $TMP/read-few.txt
336echo "'$a' '$b' '$c'"
337## STDOUT:
338'a' 'b' ''
339'a' 'b' ''
340## END
341## N-I dash STDOUT:
342'a' 'b' ''
343## END
344## BUG zsh STDOUT:
345'a' 'b' ''
346'b' '' ''
347## END
348
349#### read -r ignores backslashes
350echo 'one\ two' > $TMP/readr.txt
351read escaped < $TMP/readr.txt
352read -r raw < $TMP/readr.txt
353argv.py "$escaped" "$raw"
354## stdout: ['one two', 'one\\ two']
355
356#### read -r with other backslash escapes
357echo 'one\ two\x65three' > $TMP/readr.txt
358read escaped < $TMP/readr.txt
359read -r raw < $TMP/readr.txt
360argv.py "$escaped" "$raw"
361# mksh respects the hex escapes here, but other shells don't!
362## stdout: ['one twox65three', 'one\\ two\\x65three']
363## BUG mksh/zsh stdout: ['one twoethree', 'one\\ twoethree']
364
365#### read with line continuation reads multiple physical lines
366# NOTE: osh failing because of file descriptor issue. stdin has to be closed!
367tmp=$TMP/$(basename $SH)-readr.txt
368echo -e 'one\\\ntwo\n' > $tmp
369read escaped < $tmp
370read -r raw < $tmp
371argv.py "$escaped" "$raw"
372## stdout: ['onetwo', 'one\\']
373## N-I dash stdout: ['-e onetwo', '-e one\\']
374
375#### read multiple vars spanning many lines
376read x y << 'EOF'
377one-\
378two three-\
379four five-\
380six
381EOF
382argv.py "$x" "$y" "$z"
383## stdout: ['one-two', 'three-four five-six', '']
384
385#### read -r with \n
386echo '\nline' > $TMP/readr.txt
387read escaped < $TMP/readr.txt
388read -r raw < $TMP/readr.txt
389argv.py "$escaped" "$raw"
390# dash/mksh/zsh are bugs because at least the raw mode should let you read a
391# literal \n.
392## stdout: ['nline', '\\nline']
393## BUG dash/mksh/zsh stdout: ['', '']
394
395#### read -s from pipe, not a terminal
396case $SH in (dash|zsh) exit ;; esac
397
398# It's hard to really test this because it requires a terminal. We hit a
399# different code path when reading through a pipe. There can be bugs there
400# too!
401
402echo foo | { read -s; echo $REPLY; }
403echo bar | { read -n 2 -s; echo $REPLY; }
404
405# Hm no exit 1 here? Weird
406echo b | { read -n 2 -s; echo $?; echo $REPLY; }
407## STDOUT:
408foo
409ba
4100
411b
412## END
413## N-I dash/zsh stdout-json: ""
414
415#### read with IFS=$'\n'
416# The leading spaces are stripped if they appear in IFS.
417IFS=$(echo -e '\n')
418read var <<EOF
419 a b c
420 d e f
421EOF
422echo "[$var]"
423## stdout: [ a b c]
424## N-I dash stdout: [a b c]
425
426#### read multiple lines with IFS=:
427# The leading spaces are stripped if they appear in IFS.
428# IFS chars are escaped with :.
429tmp=$TMP/$(basename $SH)-read-ifs.txt
430IFS=:
431cat >$tmp <<'EOF'
432 \\a :b\: c:d\
433 e
434EOF
435read a b c d < $tmp
436# Use printf because echo in dash/mksh interprets escapes, while it doesn't in
437# bash.
438printf "%s\n" "[$a|$b|$c|$d]"
439## stdout: [ \a |b: c|d e|]
440
441#### read with IFS=''
442IFS=''
443read x y <<EOF
444 a b c d
445EOF
446echo "[$x|$y]"
447## stdout: [ a b c d|]
448
449#### read does not respect C backslash escapes
450
451# bash doesn't respect these, but other shells do. Gah! I think bash
452# behavior makes more sense. It only escapes IFS.
453echo '\a \b \c \d \e \f \g \h \x65 \145 \i' > $TMP/read-c.txt
454read line < $TMP/read-c.txt
455echo $line
456## stdout-json: "a b c d e f g h x65 145 i\n"
457## BUG ash stdout-json: "abcdefghx65 145 i\n"
458## BUG dash/zsh stdout-json: "\u0007 \u0008\n"
459## BUG mksh stdout-json: "\u0007 \u0008 d \u001b \u000c g h e 145 i\n"
460
461#### dynamic scope used to set vars
462f() {
463 read head << EOF
464ref: refs/heads/dev/andy
465EOF
466}
467f
468echo $head
469## STDOUT:
470ref: refs/heads/dev/andy
471## END
472
473#### read -a reads into array
474
475# read -a is used in bash-completion
476# none of these shells implement it
477case $SH in
478 *mksh|*dash|*zsh|*/ash)
479 exit 2;
480 ;;
481esac
482
483read -a myarray <<'EOF'
484a b c\ d
485EOF
486argv.py "${myarray[@]}"
487
488# arguments are ignored here
489read -r -a array2 extra arguments <<'EOF'
490a b c\ d
491EOF
492argv.py "${array2[@]}"
493argv.py "${extra[@]}"
494argv.py "${arguments[@]}"
495## status: 0
496## STDOUT:
497['a', 'b', 'c d']
498['a', 'b', 'c\\', 'd']
499[]
500[]
501## END
502## N-I dash/mksh/zsh/ash status: 2
503## N-I dash/mksh/zsh/ash stdout-json: ""
504
505#### read -d : (colon-separated records)
506printf a,b,c:d,e,f:g,h,i | {
507 IFS=,
508 read -d : v1
509 echo "v1=$v1"
510 read -d : v1 v2
511 echo "v1=$v1 v2=$v2"
512 read -d : v1 v2 v3
513 echo "v1=$v1 v2=$v2 v3=$v3"
514}
515## STDOUT:
516v1=a,b,c
517v1=d v2=e,f
518v1=g v2=h v3=i
519## END
520## N-I dash STDOUT:
521v1=
522v1= v2=
523v1= v2= v3=
524## END
525
526#### read -d '' (null-separated records)
527printf 'a,b,c\0d,e,f\0g,h,i' | {
528 IFS=,
529 read -d '' v1
530 echo "v1=$v1"
531 read -d '' v1 v2
532 echo "v1=$v1 v2=$v2"
533 read -d '' v1 v2 v3
534 echo "v1=$v1 v2=$v2 v3=$v3"
535}
536## STDOUT:
537v1=a,b,c
538v1=d v2=e,f
539v1=g v2=h v3=i
540## END
541## N-I dash STDOUT:
542v1=
543v1= v2=
544v1= v2= v3=
545## END
546
547#### read -rd
548read -rd '' var <<EOF
549foo
550bar
551EOF
552echo "$var"
553## STDOUT:
554foo
555bar
556## END
557## N-I dash stdout-json: "\n"
558
559#### read -d when there's no delimiter
560{ read -d : part
561 echo $part $?
562 read -d : part
563 echo $part $?
564} <<EOF
565foo:bar
566EOF
567## STDOUT:
568foo 0
569bar 1
570## END
571## N-I dash STDOUT:
5722
5732
574## END
575
576#### read -t 0 tests if input is available
577case $SH in (dash|zsh|mksh) exit ;; esac
578
579# is there input available?
580read -t 0 < /dev/null
581echo $?
582
583# floating point
584read -t 0.0 < /dev/null
585echo $?
586
587# floating point
588echo foo | { read -t 0; echo reply=$REPLY; }
589echo $?
590
591## STDOUT:
5920
5930
594reply=
5950
596## END
597## N-I dash/zsh/mksh stdout-json: ""
598
599#### read -t 0.5
600case $SH in (dash) exit ;; esac
601
602read -t 0.5 < /dev/null
603echo $?
604
605## STDOUT:
6061
607## END
608## BUG zsh/mksh STDOUT:
6091
610## END
611## N-I dash stdout-json: ""
612
613#### read -t -0.5 is invalid
614# bash appears to just take the absolute value?
615
616read -t -0.5 < /dev/null
617echo $?
618
619## STDOUT:
6202
621## END
622## BUG bash STDOUT:
6231
624## END
625## BUG zsh stdout-json: ""
626## BUG zsh status: 1
627
628#### read -u
629case $SH in (dash|mksh) exit ;; esac
630
631# file descriptor
632read -u 3 3<<EOF
633hi
634EOF
635echo reply=$REPLY
636## STDOUT:
637reply=hi
638## END
639## N-I dash/mksh stdout-json: ""
640
641#### read -u syntax error
642read -u -3
643echo status=$?
644## STDOUT:
645status=2
646## END
647## OK bash/zsh STDOUT:
648status=1
649## END
650
651#### read -N doesn't respect delimiter, while read -n does
652case $SH in (dash|zsh|ash) exit ;; esac
653
654echo foobar | { read -n 5 -d b; echo $REPLY; }
655echo foobar | { read -N 5 -d b; echo $REPLY; }
656## STDOUT:
657foo
658fooba
659## END
660## OK mksh STDOUT:
661fooba
662fooba
663## END
664## N-I dash/zsh/ash stdout-json: ""
665
666#### read -p (not fully tested)
667
668# hm DISABLED if we're not going to the terminal
669# so we're only testing that it accepts the flag here
670
671case $SH in (dash|mksh|zsh) exit ;; esac
672
673echo hi | { read -p 'P'; echo $REPLY; }
674echo hi | { read -p 'P' -n 1; echo $REPLY; }
675## STDOUT:
676hi
677h
678## END
679## stderr-json: ""
680## N-I dash/mksh/zsh stdout-json: ""
681
682#### read usage
683read -n -1
684echo status=$?
685## STDOUT:
686status=2
687## END
688## OK bash stdout: status=1
689## BUG mksh stdout-json: ""
690# zsh gives a fatal error? seems inconsistent
691## BUG zsh stdout-json: ""
692## BUG zsh status: 1
693
694#### read with smooshed args
695echo hi | { read -rn1 var; echo var=$var; }
696## STDOUT:
697var=h
698## END
699## N-I dash/zsh STDOUT:
700var=
701## END
702
703#### read -r -d '' for NUL strings, e.g. find -print0
704
705
706case $SH in (dash|zsh|mksh) exit ;; esac # NOT IMPLEMENTED
707
708mkdir -p read0
709cd read0
710rm -f *
711
712touch a\\b\\c\\d # -r is necessary!
713
714find . -type f -a -print0 | { read -r -d ''; echo "[$REPLY]"; }
715
716## STDOUT:
717[./a\b\c\d]
718## END
719## N-I dash/zsh/mksh STDOUT:
720## END
721
722
723#### read from redirected directory is non-fatal error
724
725# This tickles an infinite loop bug in our version of mksh! TODO: upgrade the
726# version and enable this
727case $SH in (mksh) return ;; esac
728
729cd $TMP
730mkdir -p dir
731read x < ./dir
732echo status=$?
733
734## STDOUT:
735status=1
736## END
737# OK mksh stdout: status=2
738## OK mksh stdout-json: ""
739
740#### read -n from directory
741
742case $SH in (dash|ash) return ;; esac # not implemented
743
744# same hanging bug
745case $SH in (mksh) return ;; esac
746
747mkdir -p dir
748read -n 3 x < ./dir
749echo status=$?
750## STDOUT:
751status=1
752## END
753## OK mksh stdout-json: ""
754## N-I dash/ash stdout-json: ""
755
756#### mapfile from directory (bash doesn't handle errors)
757case $SH in (dash|ash|mksh|zsh) return ;; esac # not implemented
758
759mkdir -p dir
760mapfile $x < ./dir
761echo status=$?
762
763## STDOUT:
764status=1
765## END
766## BUG bash STDOUT:
767status=0
768## END
769## N-I dash/ash/mksh/zsh stdout-json: ""