1 |
#!/bin/bash |
2 |
# |
3 |
# echo, read |
4 |
# later: perhaps mapfile, etc. |
5 |
|
6 |
#### echo dashes |
7 |
echo - |
8 |
echo -- |
9 |
echo --- |
10 |
## stdout-json: "-\n--\n---\n" |
11 |
## BUG zsh stdout-json: "\n--\n---\n" |
12 |
|
13 |
#### echo backslashes |
14 |
echo \\ |
15 |
echo '\' |
16 |
echo '\\' |
17 |
echo "\\" |
18 |
## STDOUT: |
19 |
\ |
20 |
\ |
21 |
\\ |
22 |
\ |
23 |
## BUG dash/mksh/zsh STDOUT: |
24 |
\ |
25 |
\ |
26 |
\ |
27 |
\ |
28 |
## END |
29 |
|
30 |
#### echo -e backslashes |
31 |
echo -e \\ |
32 |
echo -e '\' |
33 |
echo -e '\\' |
34 |
echo -e "\\" |
35 |
## STDOUT: |
36 |
\ |
37 |
\ |
38 |
\ |
39 |
\ |
40 |
## N-I dash STDOUT: |
41 |
-e \ |
42 |
-e \ |
43 |
-e \ |
44 |
-e \ |
45 |
## END |
46 |
|
47 |
#### echo -en |
48 |
echo -en 'abc\ndef\n' |
49 |
## stdout-json: "abc\ndef\n" |
50 |
## N-I dash stdout-json: "-en abc\ndef\n\n" |
51 |
|
52 |
#### echo -ez (invalid flag) |
53 |
# bash differs from the other three shells, but its behavior is possibly more |
54 |
# sensible, if you're going to ignore the error. It doesn't make sense for |
55 |
# the 'e' to mean 2 different things simultaneously: flag and literal to be |
56 |
# printed. |
57 |
echo -ez 'abc\n' |
58 |
## stdout-json: "-ez abc\\n\n" |
59 |
## OK dash/mksh/zsh stdout-json: "-ez abc\n\n" |
60 |
|
61 |
#### echo -e with embedded newline |
62 |
flags='-e' |
63 |
case $SH in */dash) flags='' ;; esac |
64 |
|
65 |
echo $flags 'foo |
66 |
bar' |
67 |
## STDOUT: |
68 |
foo |
69 |
bar |
70 |
## END |
71 |
|
72 |
#### echo -e line continuation |
73 |
flags='-e' |
74 |
case $SH in */dash) flags='' ;; esac |
75 |
|
76 |
echo $flags 'foo\ |
77 |
bar' |
78 |
## STDOUT: |
79 |
foo\ |
80 |
bar |
81 |
## END |
82 |
|
83 |
#### echo -e with C escapes |
84 |
# https://www.gnu.org/software/bash/manual/bashref.html#Bourne-Shell-Builtins |
85 |
# not sure why \c is like NUL? |
86 |
# zsh doesn't allow \E for some reason. |
87 |
echo -e '\a\b\d\e\f' |
88 |
## stdout-json: "\u0007\u0008\\d\u001b\u000c\n" |
89 |
## N-I dash stdout-json: "-e \u0007\u0008\\d\\e\u000c\n" |
90 |
|
91 |
#### echo -e with whitespace C escapes |
92 |
echo -e '\n\r\t\v' |
93 |
## stdout-json: "\n\r\t\u000b\n" |
94 |
## N-I dash stdout-json: "-e \n\r\t\u000b\n" |
95 |
|
96 |
#### \0 |
97 |
echo -e 'ab\0cd' |
98 |
## stdout-json: "ab\u0000cd\n" |
99 |
# dash truncates it |
100 |
## BUG dash stdout-json: "-e ab\n" |
101 |
|
102 |
#### \c stops processing input |
103 |
flags='-e' |
104 |
case $SH in */dash) flags='' ;; esac |
105 |
|
106 |
echo $flags xy 'ab\cde' 'ab\cde' |
107 |
## stdout-json: "xy ab" |
108 |
## N-I mksh stdout-json: "xy abde abde" |
109 |
|
110 |
#### echo -e with hex escape |
111 |
echo -e 'abcd\x65f' |
112 |
## stdout-json: "abcdef\n" |
113 |
## N-I dash stdout-json: "-e abcd\\x65f\n" |
114 |
|
115 |
#### echo -e with octal escape |
116 |
flags='-e' |
117 |
case $SH in */dash) flags='' ;; esac |
118 |
|
119 |
echo $flags 'abcd\044e' |
120 |
## stdout-json: "abcd$e\n" |
121 |
|
122 |
#### echo -e with 4 digit unicode escape |
123 |
flags='-e' |
124 |
case $SH in */dash) flags='' ;; esac |
125 |
|
126 |
echo $flags 'abcd\u0065f' |
127 |
## STDOUT: |
128 |
abcdef |
129 |
## END |
130 |
## N-I dash/ash stdout-json: "abcd\\u0065f\n" |
131 |
|
132 |
#### echo -e with 8 digit unicode escape |
133 |
flags='-e' |
134 |
case $SH in */dash) flags='' ;; esac |
135 |
|
136 |
echo $flags 'abcd\U00000065f' |
137 |
## STDOUT: |
138 |
abcdef |
139 |
## END |
140 |
## N-I dash/ash stdout-json: "abcd\\U00000065f\n" |
141 |
|
142 |
#### \0377 is the highest octal byte |
143 |
echo -en '\03777' | od -A n -t x1 | sed 's/ \+/ /g' |
144 |
## stdout-json: " ff 37\n" |
145 |
## N-I dash stdout-json: " 2d 65 6e 20 ff 37 0a\n" |
146 |
|
147 |
#### \0400 is one more than the highest octal byte |
148 |
# It is 256 % 256 which gets interpreted as a NUL byte. |
149 |
echo -en '\04000' | od -A n -t x1 | sed 's/ \+/ /g' |
150 |
## stdout-json: " 00 30\n" |
151 |
## BUG ash stdout-json: " 20 30 30\n" |
152 |
## N-I dash stdout-json: " 2d 65 6e 20\n" |
153 |
|
154 |
#### \0777 is out of range |
155 |
flags='-en' |
156 |
case $SH in */dash) flags='-n' ;; esac |
157 |
|
158 |
echo $flags '\0777' | od -A n -t x1 | sed 's/ \+/ /g' |
159 |
## stdout-json: " ff\n" |
160 |
## BUG mksh stdout-json: " c3 bf\n" |
161 |
## BUG ash stdout-json: " 3f 37\n" |
162 |
|
163 |
#### incomplete hex escape |
164 |
echo -en 'abcd\x6' | od -A n -c | sed 's/ \+/ /g' |
165 |
## stdout-json: " a b c d 006\n" |
166 |
## N-I dash stdout-json: " - e n a b c d \\ x 6 \\n\n" |
167 |
|
168 |
#### \x |
169 |
# I consider mksh and zsh a bug because \x is not an escape |
170 |
echo -e '\x' '\xg' | od -A n -c | sed 's/ \+/ /g' |
171 |
## stdout-json: " \\ x \\ x g \\n\n" |
172 |
## N-I dash stdout-json: " - e \\ x \\ x g \\n\n" |
173 |
## BUG mksh/zsh stdout-json: " \\0 \\0 g \\n\n" |
174 |
|
175 |
#### incomplete octal escape |
176 |
flags='-en' |
177 |
case $SH in */dash) flags='-n' ;; esac |
178 |
|
179 |
echo $flags 'abcd\04' | od -A n -c | sed 's/ \+/ /g' |
180 |
## stdout-json: " a b c d 004\n" |
181 |
|
182 |
#### incomplete unicode escape |
183 |
echo -en 'abcd\u006' | od -A n -c | sed 's/ \+/ /g' |
184 |
## stdout-json: " a b c d 006\n" |
185 |
## N-I dash stdout-json: " - e n a b c d \\ u 0 0 6 \\n\n" |
186 |
## BUG ash stdout-json: " a b c d \\ u 0 0 6\n" |
187 |
|
188 |
#### \u6 |
189 |
flags='-en' |
190 |
case $SH in */dash) flags='-n' ;; esac |
191 |
|
192 |
echo $flags '\u6' | od -A n -c | sed 's/ \+/ /g' |
193 |
## stdout-json: " 006\n" |
194 |
## N-I dash/ash stdout-json: " \\ u 6\n" |
195 |
|
196 |
#### \0 \1 \8 |
197 |
# \0 is special, but \1 isn't in bash |
198 |
# \1 is special in dash! geez |
199 |
flags='-en' |
200 |
case $SH in */dash) flags='-n' ;; esac |
201 |
|
202 |
echo $flags '\0' '\1' '\8' | od -A n -c | sed 's/ \+/ /g' |
203 |
## stdout-json: " \\0 \\ 1 \\ 8\n" |
204 |
## BUG dash stdout-json: " 001 \\ 8\n" |
205 |
## BUG ash stdout-json: " \\0 001 \\ 8\n" |
206 |
|
207 |
#### Read builtin |
208 |
# NOTE: there are TABS below |
209 |
read x <<EOF |
210 |
A B C D E |
211 |
FG |
212 |
EOF |
213 |
echo "[$x]" |
214 |
## stdout: [A B C D E] |
215 |
## status: 0 |
216 |
|
217 |
#### Read from empty file |
218 |
echo -n '' > $TMP/empty.txt |
219 |
read x < $TMP/empty.txt |
220 |
argv.py "status=$?" "$x" |
221 |
## stdout: ['status=1', ''] |
222 |
## status: 0 |
223 |
|
224 |
#### Read builtin with no newline. |
225 |
# This is odd because the variable is populated successfully. OSH/Oil might |
226 |
# need a separate put reading feature that doesn't use IFS. |
227 |
echo -n ZZZ | { read x; echo $?; echo $x; } |
228 |
## stdout-json: "1\nZZZ\n" |
229 |
## status: 0 |
230 |
|
231 |
#### Read builtin with multiple variables |
232 |
# NOTE: there are TABS below |
233 |
read x y z <<EOF |
234 |
A B C D E |
235 |
FG |
236 |
EOF |
237 |
echo "[$x/$y/$z]" |
238 |
## stdout: [A/B/C D E] |
239 |
## BUG dash stdout: [A/B/C D E ] |
240 |
## status: 0 |
241 |
|
242 |
#### Read builtin with not enough variables |
243 |
set -o errexit |
244 |
set -o nounset # hm this doesn't change it |
245 |
read x y z <<EOF |
246 |
A B |
247 |
EOF |
248 |
echo /$x/$y/$z/ |
249 |
## stdout: /A/B// |
250 |
## status: 0 |
251 |
|
252 |
#### Read -n (with $REPLY) |
253 |
echo 12345 > $TMP/readn.txt |
254 |
read -n 4 x < $TMP/readn.txt |
255 |
read -n 2 < $TMP/readn.txt # Do it again with no variable |
256 |
argv.py $x $REPLY |
257 |
## stdout: ['1234', '12'] |
258 |
## N-I dash/zsh stdout: [] |
259 |
|
260 |
#### Read uses $REPLY (without -n) |
261 |
echo 123 > $TMP/readreply.txt |
262 |
read < $TMP/readreply.txt |
263 |
echo $REPLY |
264 |
## stdout: 123 |
265 |
## N-I dash stdout: |
266 |
|
267 |
#### read -r ignores backslashes |
268 |
echo 'one\ two' > $TMP/readr.txt |
269 |
read escaped < $TMP/readr.txt |
270 |
read -r raw < $TMP/readr.txt |
271 |
argv.py "$escaped" "$raw" |
272 |
## stdout: ['one two', 'one\\ two'] |
273 |
|
274 |
#### read -r with other backslash escapes |
275 |
echo 'one\ two\x65three' > $TMP/readr.txt |
276 |
read escaped < $TMP/readr.txt |
277 |
read -r raw < $TMP/readr.txt |
278 |
argv.py "$escaped" "$raw" |
279 |
# mksh respects the hex escapes here, but other shells don't! |
280 |
## stdout: ['one twox65three', 'one\\ two\\x65three'] |
281 |
## BUG mksh/zsh stdout: ['one twoethree', 'one\\ twoethree'] |
282 |
|
283 |
#### read with line continuation reads multiple physical lines |
284 |
# NOTE: osh failing because of file descriptor issue. stdin has to be closed! |
285 |
tmp=$TMP/$(basename $SH)-readr.txt |
286 |
echo -e 'one\\\ntwo\n' > $tmp |
287 |
read escaped < $tmp |
288 |
read -r raw < $tmp |
289 |
argv.py "$escaped" "$raw" |
290 |
## stdout: ['onetwo', 'one\\'] |
291 |
## N-I dash stdout: ['-e onetwo', '-e one\\'] |
292 |
|
293 |
#### read multiple vars spanning many lines |
294 |
read x y << 'EOF' |
295 |
one-\ |
296 |
two three-\ |
297 |
four five-\ |
298 |
six |
299 |
EOF |
300 |
argv.py "$x" "$y" "$z" |
301 |
## stdout: ['one-two', 'three-four five-six', ''] |
302 |
|
303 |
#### read -r with \n |
304 |
echo '\nline' > $TMP/readr.txt |
305 |
read escaped < $TMP/readr.txt |
306 |
read -r raw < $TMP/readr.txt |
307 |
argv.py "$escaped" "$raw" |
308 |
# dash/mksh/zsh are bugs because at least the raw mode should let you read a |
309 |
# literal \n. |
310 |
## stdout: ['nline', '\\nline'] |
311 |
## BUG dash/mksh/zsh stdout: ['', ''] |
312 |
|
313 |
#### Read with IFS=$'\n' |
314 |
# The leading spaces are stripped if they appear in IFS. |
315 |
IFS=$(echo -e '\n') |
316 |
read var <<EOF |
317 |
a b c |
318 |
d e f |
319 |
EOF |
320 |
echo "[$var]" |
321 |
## stdout: [ a b c] |
322 |
## N-I dash stdout: [a b c] |
323 |
|
324 |
#### Read multiple lines with IFS=: |
325 |
# The leading spaces are stripped if they appear in IFS. |
326 |
# IFS chars are escaped with :. |
327 |
tmp=$TMP/$(basename $SH)-read-ifs.txt |
328 |
IFS=: |
329 |
cat >$tmp <<'EOF' |
330 |
\\a :b\: c:d\ |
331 |
e |
332 |
EOF |
333 |
read a b c d < $tmp |
334 |
# Use printf because echo in dash/mksh interprets escapes, while it doesn't in |
335 |
# bash. |
336 |
printf "%s\n" "[$a|$b|$c|$d]" |
337 |
## stdout: [ \a |b: c|d e|] |
338 |
|
339 |
#### Read with IFS='' |
340 |
IFS='' |
341 |
read x y <<EOF |
342 |
a b c d |
343 |
EOF |
344 |
echo "[$x|$y]" |
345 |
## stdout: [ a b c d|] |
346 |
|
347 |
#### Read should not respect C escapes. |
348 |
# bash doesn't respect these, but other shells do. Gah! I think bash |
349 |
# behavior makes more sense. It only escapes IFS. |
350 |
echo '\a \b \c \d \e \f \g \h \x65 \145 \i' > $TMP/read-c.txt |
351 |
read line < $TMP/read-c.txt |
352 |
echo $line |
353 |
## stdout-json: "a b c d e f g h x65 145 i\n" |
354 |
## BUG ash stdout-json: "abcdefghx65 145 i\n" |
355 |
## BUG dash/zsh stdout-json: "\u0007 \u0008\n" |
356 |
## BUG mksh stdout-json: "\u0007 \u0008 d \u001b \u000c g h e 145 i\n" |
357 |
|
358 |
#### Read builtin uses dynamic scope |
359 |
f() { |
360 |
read head << EOF |
361 |
ref: refs/heads/dev/andy |
362 |
EOF |
363 |
} |
364 |
f |
365 |
echo $head |
366 |
## STDOUT: |
367 |
ref: refs/heads/dev/andy |
368 |
## END |
369 |
|
370 |
#### read -a reads into array |
371 |
|
372 |
# read -a is used in bash-completion |
373 |
# none of these shells implement it |
374 |
case $SH in |
375 |
*mksh|*dash|*zsh|*/ash) |
376 |
exit 2; |
377 |
;; |
378 |
esac |
379 |
|
380 |
read -a myarray <<'EOF' |
381 |
a b c\ d |
382 |
EOF |
383 |
argv.py "${myarray[@]}" |
384 |
|
385 |
# arguments are ignored here |
386 |
read -r -a array2 extra arguments <<'EOF' |
387 |
a b c\ d |
388 |
EOF |
389 |
argv.py "${array2[@]}" |
390 |
argv.py "${extra[@]}" |
391 |
argv.py "${arguments[@]}" |
392 |
## status: 0 |
393 |
## STDOUT: |
394 |
['a', 'b', 'c d'] |
395 |
['a', 'b', 'c\\', 'd'] |
396 |
[] |
397 |
[] |
398 |
## END |
399 |
## N-I dash/mksh/zsh/ash status: 2 |
400 |
## N-I dash/mksh/zsh/ash stdout-json: "" |
401 |
|
402 |
#### read -n with invalid arg |
403 |
read -n not_a_number |
404 |
echo status=$? |
405 |
## stdout: status=2 |
406 |
## OK bash stdout: status=1 |
407 |
## N-I zsh stdout-json: "" |
408 |
|
409 |
#### read returns correct number of bytes without EOF |
410 |
case $SH in |
411 |
*bash|*osh) FLAG=n ;; |
412 |
*mksh) FLAG=N ;; |
413 |
*) exit ;; # other shells don't implement it, or hang |
414 |
esac |
415 |
|
416 |
i=0 |
417 |
while true; do |
418 |
echo -n x |
419 |
|
420 |
(( i++ )) |
421 |
|
422 |
# TODO: Why does OSH hang without this test? Other shells are fine. I can't |
423 |
# reproduce outside of sh_spec.py. |
424 |
if test $i = 100; then |
425 |
break |
426 |
#true |
427 |
fi |
428 |
done | { read -$FLAG 3; echo $REPLY; } |
429 |
|
430 |
## status: 0 |
431 |
## stdout: xxx |
432 |
## N-I dash/ash stdout-json: "" |
433 |
|
434 |
# zsh appears to hang with -k |
435 |
## N-I zsh stdout-json: "" |