OILS / spec / builtin-read.test.sh View on Github | oils.pub

775 lines, 346 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:
457a b c d e f g h x65 145 i
458## END
459## BUG ash STDOUT:
460abcdefghx65 145 i
461## END
462## BUG dash/zsh stdout-json: "\u0007 \u0008\n"
463## BUG mksh stdout-json: "\u0007 \u0008 d \u001b \u000c g h e 145 i\n"
464
465#### dynamic scope used to set vars
466f() {
467 read head << EOF
468ref: refs/heads/dev/andy
469EOF
470}
471f
472echo $head
473## STDOUT:
474ref: refs/heads/dev/andy
475## END
476
477#### read -a reads into array
478
479# read -a is used in bash-completion
480# none of these shells implement it
481case $SH in
482 *mksh|*dash|*zsh|*/ash)
483 exit 2;
484 ;;
485esac
486
487read -a myarray <<'EOF'
488a b c\ d
489EOF
490argv.py "${myarray[@]}"
491
492# arguments are ignored here
493read -r -a array2 extra arguments <<'EOF'
494a b c\ d
495EOF
496argv.py "${array2[@]}"
497argv.py "${extra[@]}"
498argv.py "${arguments[@]}"
499## status: 0
500## STDOUT:
501['a', 'b', 'c d']
502['a', 'b', 'c\\', 'd']
503[]
504[]
505## END
506## N-I dash/mksh/zsh/ash status: 2
507## N-I dash/mksh/zsh/ash stdout-json: ""
508
509#### read -d : (colon-separated records)
510printf a,b,c:d,e,f:g,h,i | {
511 IFS=,
512 read -d : v1
513 echo "v1=$v1"
514 read -d : v1 v2
515 echo "v1=$v1 v2=$v2"
516 read -d : v1 v2 v3
517 echo "v1=$v1 v2=$v2 v3=$v3"
518}
519## STDOUT:
520v1=a,b,c
521v1=d v2=e,f
522v1=g v2=h v3=i
523## END
524## N-I dash STDOUT:
525v1=
526v1= v2=
527v1= v2= v3=
528## END
529
530#### read -d '' (null-separated records)
531printf 'a,b,c\0d,e,f\0g,h,i' | {
532 IFS=,
533 read -d '' v1
534 echo "v1=$v1"
535 read -d '' v1 v2
536 echo "v1=$v1 v2=$v2"
537 read -d '' v1 v2 v3
538 echo "v1=$v1 v2=$v2 v3=$v3"
539}
540## STDOUT:
541v1=a,b,c
542v1=d v2=e,f
543v1=g v2=h v3=i
544## END
545## N-I dash STDOUT:
546v1=
547v1= v2=
548v1= v2= v3=
549## END
550
551#### read -rd
552read -rd '' var <<EOF
553foo
554bar
555EOF
556echo "$var"
557## STDOUT:
558foo
559bar
560## END
561## N-I dash STDOUT:
562
563## END
564
565#### read -d when there's no delimiter
566{ read -d : part
567 echo $part $?
568 read -d : part
569 echo $part $?
570} <<EOF
571foo:bar
572EOF
573## STDOUT:
574foo 0
575bar 1
576## END
577## N-I dash STDOUT:
5782
5792
580## END
581
582#### read -t 0 tests if input is available
583case $SH in (dash|zsh|mksh) exit ;; esac
584
585# is there input available?
586read -t 0 < /dev/null
587echo $?
588
589# floating point
590read -t 0.0 < /dev/null
591echo $?
592
593# floating point
594echo foo | { read -t 0; echo reply=$REPLY; }
595echo $?
596
597## STDOUT:
5980
5990
600reply=
6010
602## END
603## N-I dash/zsh/mksh stdout-json: ""
604
605#### read -t 0.5
606case $SH in (dash) exit ;; esac
607
608read -t 0.5 < /dev/null
609echo $?
610
611## STDOUT:
6121
613## END
614## BUG zsh/mksh STDOUT:
6151
616## END
617## N-I dash stdout-json: ""
618
619#### read -t -0.5 is invalid
620# bash appears to just take the absolute value?
621
622read -t -0.5 < /dev/null
623echo $?
624
625## STDOUT:
6262
627## END
628## BUG bash STDOUT:
6291
630## END
631## BUG zsh stdout-json: ""
632## BUG zsh status: 1
633
634#### read -u
635case $SH in (dash|mksh) exit ;; esac
636
637# file descriptor
638read -u 3 3<<EOF
639hi
640EOF
641echo reply=$REPLY
642## STDOUT:
643reply=hi
644## END
645## N-I dash/mksh stdout-json: ""
646
647#### read -u syntax error
648read -u -3
649echo status=$?
650## STDOUT:
651status=2
652## END
653## OK bash/zsh STDOUT:
654status=1
655## END
656
657#### read -N doesn't respect delimiter, while read -n does
658case $SH in (dash|zsh|ash) exit ;; esac
659
660echo foobar | { read -n 5 -d b; echo $REPLY; }
661echo foobar | { read -N 5 -d b; echo $REPLY; }
662## STDOUT:
663foo
664fooba
665## END
666## OK mksh STDOUT:
667fooba
668fooba
669## END
670## N-I dash/zsh/ash stdout-json: ""
671
672#### read -p (not fully tested)
673
674# hm DISABLED if we're not going to the terminal
675# so we're only testing that it accepts the flag here
676
677case $SH in (dash|mksh|zsh) exit ;; esac
678
679echo hi | { read -p 'P'; echo $REPLY; }
680echo hi | { read -p 'P' -n 1; echo $REPLY; }
681## STDOUT:
682hi
683h
684## END
685## stderr-json: ""
686## N-I dash/mksh/zsh stdout-json: ""
687
688#### read usage
689read -n -1
690echo status=$?
691## STDOUT:
692status=2
693## END
694## OK bash stdout: status=1
695## BUG mksh stdout-json: ""
696# zsh gives a fatal error? seems inconsistent
697## BUG zsh stdout-json: ""
698## BUG zsh status: 1
699
700#### read with smooshed args
701echo hi | { read -rn1 var; echo var=$var; }
702## STDOUT:
703var=h
704## END
705## N-I dash/zsh STDOUT:
706var=
707## END
708
709#### read -r -d '' for NUL strings, e.g. find -print0
710
711
712case $SH in (dash|zsh|mksh) exit ;; esac # NOT IMPLEMENTED
713
714mkdir -p read0
715cd read0
716rm -f *
717
718touch a\\b\\c\\d # -r is necessary!
719
720find . -type f -a -print0 | { read -r -d ''; echo "[$REPLY]"; }
721
722## STDOUT:
723[./a\b\c\d]
724## END
725## N-I dash/zsh/mksh STDOUT:
726## END
727
728
729#### read from redirected directory is non-fatal error
730
731# This tickles an infinite loop bug in our version of mksh! TODO: upgrade the
732# version and enable this
733case $SH in (mksh) return ;; esac
734
735cd $TMP
736mkdir -p dir
737read x < ./dir
738echo status=$?
739
740## STDOUT:
741status=1
742## END
743# OK mksh stdout: status=2
744## OK mksh stdout-json: ""
745
746#### read -n from directory
747
748case $SH in (dash|ash) return ;; esac # not implemented
749
750# same hanging bug
751case $SH in (mksh) return ;; esac
752
753mkdir -p dir
754read -n 3 x < ./dir
755echo status=$?
756## STDOUT:
757status=1
758## END
759## OK mksh stdout-json: ""
760## N-I dash/ash stdout-json: ""
761
762#### mapfile from directory (bash doesn't handle errors)
763case $SH in (dash|ash|mksh|zsh) return ;; esac # not implemented
764
765mkdir -p dir
766mapfile $x < ./dir
767echo status=$?
768
769## STDOUT:
770status=1
771## END
772## BUG bash STDOUT:
773status=0
774## END
775## N-I dash/ash/mksh/zsh stdout-json: ""