568 lines
20 KiB
YAML
568 lines
20 KiB
YAML
name: "Builtins: read"
|
|
cases:
|
|
- name: "Basic read usage from file"
|
|
test_files:
|
|
- path: "data.txt"
|
|
contents: |
|
|
a 1
|
|
b 2
|
|
stdin: |
|
|
while read name num; do echo "Hello, $name => $num"; done < data.txt
|
|
echo "Exited loop; name='$name', num='$num'"
|
|
|
|
- name: "Basic read usage from file and no var name"
|
|
test_files:
|
|
- path: "data.txt"
|
|
contents: |
|
|
a 1
|
|
b 2
|
|
stdin: |
|
|
while read; do echo "reply: '$REPLY'"; done < data.txt
|
|
echo "Exited loop; reply='$REPLY'"
|
|
|
|
- name: "Basic read usage with empty file"
|
|
stdin: |
|
|
while read name num; do echo "Hello, $name => $num"; done < /dev/null
|
|
declare -p name num
|
|
|
|
while read; do echo "reply: '$REPLY'"; done < /dev/null
|
|
declare -p REPLY
|
|
|
|
while read -a arr; do declare -p arr; done < /dev/null
|
|
declare -p arr
|
|
|
|
- name: "Basic read usage from pipe"
|
|
stdin: |
|
|
echo "1."
|
|
(echo a; echo b) | while read name; do echo "Hello, $name"; done
|
|
|
|
echo "2."
|
|
(echo "a b") | while read name; do echo "Hello, 1:$name REPLY:$REPLY"; done
|
|
|
|
echo "3."
|
|
(echo "a b") | while read -a arr; do declare -p arr; done
|
|
|
|
- name: "read with closed stdin"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo 1 | read <&-
|
|
echo "result: $?"
|
|
|
|
- name: "read from here string"
|
|
stdin: |
|
|
read myvar <<< "hello"
|
|
echo "myvar: ${myvar}"
|
|
|
|
- name: "read from process substitution"
|
|
stdin: |
|
|
read myvar < <(echo hello)
|
|
echo "myvar: ${myvar}"
|
|
|
|
- name: "read with custom IFS"
|
|
stdin: |
|
|
content=" a b c "
|
|
while IFS= read line; do
|
|
echo "LINE: '$line'"
|
|
done <<<"${content}"
|
|
|
|
- name: "read text with tabs and custom IFS"
|
|
stdin: |
|
|
while IFS="" read myvar; do
|
|
echo "myvar1: |${myvar}|"
|
|
done < <(printf "a\tb\nc d\te\n")
|
|
|
|
- name: "read with empty entries"
|
|
stdin: |
|
|
(echo "x"; echo ""; echo "y"; echo ""; echo "") | while read line; do echo "LINE: '$line'"; done
|
|
echo 'x,,y,z' | (IFS=',' read READ1 READ2 READ3 READ4; declare -p READ1; declare -p READ2; declare -p READ3; declare -p READ4)
|
|
|
|
- name: "read -a with empty lines"
|
|
stdin: |
|
|
(echo "hi"; echo ""; echo "there"; echo ""; echo "you") | (read -a READ; declare -p READ)
|
|
(echo -e "hi\t\tthere\t\tyou") | (read -a READ -d $'\t'; declare -p READ)
|
|
|
|
- name: "read -a with empty interior field"
|
|
stdin: |
|
|
echo -e -n "hi||there||you" | (IFS='|' read -a READ; declare -p READ)
|
|
echo -e -n "hi there you " | (IFS=' ' read -a READ; declare -p READ)
|
|
echo -e -n "hi there you " | (read -a READ; declare -p READ)
|
|
|
|
- name: "read -a with empty leading field"
|
|
stdin: |
|
|
echo -e -n "|hi|there|you" | (IFS='|' read -a READ; declare -p READ)
|
|
|
|
- name: "read -a with empty trailing field"
|
|
stdin: |
|
|
echo -e -n "hi|there|you|" | (IFS='|' read -a READ; declare -p READ)
|
|
|
|
- name: "read -a with empty entries + empty delimiter"
|
|
stdin: |
|
|
(echo "hi"; echo ""; echo "there"; echo ""; echo "you") | (read -a READ -d ''; declare -p READ)
|
|
echo -e -n "hi\n\nthere\n\nyou\n" | (read -a READ -d ''; declare -p READ)
|
|
|
|
- name: "read -a with empty entries + empty delimiter + custom IFS"
|
|
stdin: |
|
|
(echo 'x,,y,z'; echo 'w,v'; echo ''; echo ''; echo 'm') | (IFS=',' read -d "" -a READ; declare -p READ)
|
|
|
|
- name: "read with empty delimiter"
|
|
min_oracle_version: 5.2 # \n renders differently in older shell versions
|
|
stdin: |
|
|
echo x | (read -d ""; declare -p REPLY)
|
|
|
|
# Timeout tests (read -t)
|
|
- name: "read -t 0 with data available in pipe"
|
|
# TODO(read):
|
|
# This test is racy - poll(0) may check the pipe before echo's data arrives.
|
|
# Bash consistently wins this race; our implementation sometimes doesn't.
|
|
# Worth investigating.
|
|
skip: true
|
|
stdin: |
|
|
echo "test" | { read -t 0; echo "exit: $?"; }
|
|
|
|
- name: "read -t 0 with no data (empty pipe)"
|
|
skip: true # TODO(read): see above
|
|
stdin: |
|
|
# Closed pipe with no data
|
|
: | { read -t 0; echo "exit: $?"; }
|
|
|
|
- name: "read -t with data immediately available"
|
|
stdin: |
|
|
echo "hello world" | { read -t 1 line; echo "exit: $?, line: '$line'"; }
|
|
|
|
- name: "read -t with fractional timeout and data available"
|
|
stdin: |
|
|
echo "test data" | { read -t 0.5 var; echo "exit: $?, var: '$var'"; }
|
|
|
|
- name: "read -t with regular file (always ready)"
|
|
test_files:
|
|
- path: "data.txt"
|
|
contents: |
|
|
line1
|
|
line2
|
|
stdin: |
|
|
read -t 0 < data.txt; echo "exit: $?"
|
|
read -t 1 line < data.txt; echo "exit: $?, line: '$line'"
|
|
|
|
- name: "read -t negative timeout is invalid"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
read -t -1
|
|
echo "exit: $?"
|
|
|
|
- name: "read rejects an infinite or NaN timeout"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
for t in inf -inf +inf Infinity nan NaN 1e400; do
|
|
echo x | { read -t "$t" v; echo "$t: rc=$? v='$v'"; }
|
|
done
|
|
|
|
- name: "read rejects a timeout that isn't a decimal number"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
for t in 0x10 1:2 1.5.2 ++1 ' 1' '1 '; do
|
|
echo x | { read -t "$t" v; echo "[$t]: rc=$? v='$v'"; }
|
|
done
|
|
|
|
- name: "read accepts a timeout too long to represent"
|
|
incompatible_platforms: ["macos"] # bash fails with EINVAL there; brush waits
|
|
stdin: |
|
|
for t in 9223372036854775807 99999999999999999999; do
|
|
echo x | { read -t "$t" v; echo "$t: rc=$? v='$v'"; }
|
|
done
|
|
|
|
- name: "read treats an empty timeout as zero"
|
|
known_failure: true # TODO(read): an empty timeout is rejected as invalid
|
|
ignore_stderr: true
|
|
stdin: |
|
|
for t in '' .; do
|
|
read -t "$t" v <<< x; echo "[$t]: rc=$? v='$v'"
|
|
done
|
|
|
|
- name: "read -t with -n option"
|
|
stdin: |
|
|
echo "abcdef" | { read -t 1 -n 3 var; echo "exit: $?, var: '$var'"; }
|
|
|
|
- name: "read -t 0 with here string"
|
|
stdin: |
|
|
read -t 0 <<< "hello"; echo "exit: $?"
|
|
read -t 1 var <<< "hello"; echo "exit: $?, var: '$var'"
|
|
|
|
# Raw mode tests (read -r)
|
|
- name: "read -r preserves backslashes"
|
|
stdin: |
|
|
echo 'hello\nworld' | { read -r line; echo "line: '$line'"; }
|
|
echo 'path\\to\\file' | { read -r line; echo "line: '$line'"; }
|
|
|
|
- name: "read without -r processes escape sequences"
|
|
stdin: |
|
|
echo 'hello\nworld' | { read line; echo "line: '$line'"; }
|
|
echo 'a\\b' | { read line; echo "line: '$line'"; }
|
|
|
|
- name: "read -r with trailing backslash"
|
|
stdin: |
|
|
printf 'line with trailing\\' | { read -r line; echo "line: '$line'"; }
|
|
|
|
- name: "read without -r line continuation"
|
|
stdin: |
|
|
# Backslash-newline continues to next line
|
|
printf 'first\\\nsecond' | { read line; echo "line: '$line'"; }
|
|
|
|
- name: "read -r does not do line continuation"
|
|
stdin: |
|
|
printf 'first\\\nsecond' | { read -r line; echo "line: '$line'"; }
|
|
|
|
# -N vs -n option tests
|
|
- name: "read -n respects delimiter"
|
|
stdin: |
|
|
# -n stops at delimiter even before char count
|
|
echo "ab:cd" | { read -n 5 var; echo "var: '$var'"; }
|
|
|
|
- name: "read -N ignores delimiter"
|
|
skip: true # TODO(read): test sometimes results in printf error
|
|
stdin: |
|
|
# -N reads exactly N chars, delimiter treated as regular char
|
|
printf "ab:cd" | { read -N 4 var; echo "var: '$var'"; }
|
|
|
|
- name: "read -N does not split by IFS"
|
|
stdin: |
|
|
# -N should not split result by IFS
|
|
printf "a b c" | { read -N 5 var; echo "var: '$var'"; }
|
|
|
|
- name: "read -N with array"
|
|
stdin: |
|
|
# With -a and -N, entire input goes to single array element
|
|
printf "a b c" | { read -N 5 -a arr; declare -p arr; }
|
|
|
|
# Exit code tests
|
|
- name: "read exit code on success"
|
|
stdin: |
|
|
echo "data" | { read var; echo "exit: $?"; }
|
|
|
|
- name: "read exit code on EOF"
|
|
stdin: |
|
|
# EOF returns non-zero
|
|
read var < /dev/null; echo "exit: $?"
|
|
|
|
- name: "read exit code on EOF with partial data"
|
|
stdin: |
|
|
# EOF with partial data still returns non-zero but assigns
|
|
printf "partial" | { read var; echo "exit: $?, var: '$var'"; }
|
|
|
|
# IFS edge cases
|
|
- name: "read with IFS containing both whitespace and non-whitespace"
|
|
stdin: |
|
|
echo "a:b c:d" | (IFS=': ' read v1 v2 v3 v4; echo "v1='$v1' v2='$v2' v3='$v3' v4='$v4'")
|
|
|
|
- name: "read with consecutive non-whitespace delimiters"
|
|
stdin: |
|
|
echo "a::b" | (IFS=':' read v1 v2 v3; echo "v1='$v1' v2='$v2' v3='$v3'")
|
|
|
|
- name: "read assignment to fewer variables than fields"
|
|
stdin: |
|
|
echo "a b c d e" | { read v1 v2; echo "v1='$v1' v2='$v2'"; }
|
|
|
|
- name: "read assignment to more variables than fields"
|
|
stdin: |
|
|
echo "a b" | { read v1 v2 v3 v4; echo "v1='$v1' v2='$v2' v3='$v3' v4='$v4'"; }
|
|
|
|
- name: "read last variable preserves original delimiters"
|
|
stdin: |
|
|
# Last variable should get remainder with original delimiters, not re-joined with space
|
|
echo "x:y:z:w" | (IFS=':' read a b; echo "a='$a' b='$b'")
|
|
echo "x:y::z:w" | (IFS=':' read a b; echo "a='$a' b='$b'")
|
|
echo "a:b c:d e" | (IFS=': ' read v1 v2; echo "v1='$v1' v2='$v2'")
|
|
|
|
- name: "read with consecutive delimiters at field boundary"
|
|
stdin: |
|
|
# When field limit causes boundary at consecutive delimiters,
|
|
# extra delimiters should be preserved in remainder.
|
|
echo "x::y" | (IFS=':' read a b; echo "a='$a' b='$b'")
|
|
echo "x:::y" | (IFS=':' read a b; echo "a='$a' b='$b'")
|
|
# Also test with more than 2 variables
|
|
echo "a::b:c" | (IFS=':' read v1 v2 v3; echo "v1='$v1' v2='$v2' v3='$v3'")
|
|
|
|
- name: "read with empty IFS preserves entire line"
|
|
stdin: |
|
|
# Empty IFS means no splitting - entire line goes to first variable
|
|
echo "a b c" | (IFS='' read v1 v2 v3; echo "v1='$v1' v2='$v2' v3='$v3'")
|
|
echo "a:b:c" | (IFS='' read v1 v2; echo "v1='$v1' v2='$v2'")
|
|
# With array
|
|
echo "a b c" | (IFS='' read -a arr; declare -p arr)
|
|
|
|
- name: "read REPLY preserves leading and trailing whitespace"
|
|
stdin: |
|
|
# When no variable names given, REPLY should preserve whitespace
|
|
read <<< ' hello world '
|
|
echo "REPLY='$REPLY'"
|
|
read <<< ' tab separated '
|
|
echo "REPLY='$REPLY'"
|
|
|
|
- name: "read -a clears pre-existing array elements"
|
|
stdin: |
|
|
# Array should be cleared before assignment, not merged
|
|
arr=(old1 old2 old3 old4 old5)
|
|
read -a arr <<< 'new1 new2'
|
|
declare -p arr
|
|
|
|
- name: "read -N still processes backslashes without -r"
|
|
stdin: |
|
|
# -N ignores delimiter but still does backslash processing
|
|
printf 'a\\nb' | { read -N 4 var; echo "var='$var'"; }
|
|
printf 'a\\\\b' | { read -N 4 var; echo "var='$var'"; }
|
|
|
|
- name: "read -r -N for truly raw fixed-length reads"
|
|
stdin: |
|
|
# -r -N should preserve backslashes exactly
|
|
printf 'a\\nb' | { read -r -N 4 var; echo "var='$var'"; }
|
|
printf 'a\\\\b' | { read -r -N 4 var; echo "var='$var'"; }
|
|
|
|
- name: "read -u with invalid file descriptor"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
exec 99<&- # fd 99 must be closed, but the harness can leak one in
|
|
read -u 99 var
|
|
echo "exit: $?"
|
|
|
|
- name: "read into readonly variable"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
readonly myvar="original"
|
|
echo "test" | read myvar
|
|
echo "exit: $?, myvar='$myvar'"
|
|
|
|
- name: "read with mixed IFS into fewer variables"
|
|
stdin: |
|
|
# Complex IFS with fewer variables - verify remainder handling
|
|
echo 'x: y : z : w' | (IFS=': ' read a b; echo "a='$a' b='$b'")
|
|
|
|
# Backslash escape counting tests (bash counts OUTPUT chars, not INPUT)
|
|
- name: "read -n counts output chars after escape processing"
|
|
stdin: |
|
|
# Bash counts OUTPUT characters toward -n limit (after escape processing).
|
|
# Input: a, \, b, c = 4 input bytes. With -n 3:
|
|
# 'a' (output 1), '\b' → 'b' (output 2), 'c' (output 3) = "abc"
|
|
printf 'a\\bc' | { read -n 3 var; echo "var='$var'"; }
|
|
# Input: \, \, a, b = 4 input bytes. With -n 3:
|
|
# '\\' → '\' (output 1), 'a' (output 2), 'b' (output 3) = "\ab"
|
|
printf '\\\\ab' | { read -n 3 var; echo "var='$var'"; }
|
|
|
|
- name: "read -n with backslash-newline continuation"
|
|
stdin: |
|
|
# Backslash-newline is line continuation (consumes both, outputs nothing).
|
|
# Input: a, b, \, \n, c = 5 input bytes. With -n 3:
|
|
# 'a' (output 1), 'b' (output 2), '\n' → continuation, 'c' (output 3) = "abc"
|
|
printf 'ab\\\nc' | { read -n 3 var; echo "var='$var'"; }
|
|
|
|
- name: "read trailing backslash discarded on EOF"
|
|
stdin: |
|
|
# Bash discards a pending backslash when EOF is reached
|
|
printf 'ab\\' | { read var; echo "var='$var'"; }
|
|
printf 'ab\\' | { read -n 5 var; echo "var='$var'"; }
|
|
|
|
# Prompt tests (read -p)
|
|
- name: "read -p reads input correctly"
|
|
stdin: |
|
|
# Test that -p option reads input correctly (prompt display behavior
|
|
# differs between bash and brush when stdin is not a terminal)
|
|
read -p "Enter: " var <<< "hello"
|
|
echo "var='$var'"
|
|
|
|
# File descriptor tests (read -u)
|
|
- name: "read -u with valid file descriptor"
|
|
stdin: |
|
|
# Read from a specific file descriptor
|
|
exec 3<<< "from fd 3"
|
|
read -u 3 var
|
|
echo "var='$var'"
|
|
exec 3<&-
|
|
|
|
- name: "read -u combined with other options"
|
|
stdin: |
|
|
exec 4<<< "hello world test"
|
|
read -u 4 -a arr
|
|
declare -p arr
|
|
exec 4<&-
|
|
|
|
# Silent mode tests (read -s)
|
|
- name: "read -s with pipe input"
|
|
stdin: |
|
|
# Silent mode should still read input correctly (just not echo it)
|
|
echo "secret" | { read -s var; echo "var='$var'"; }
|
|
|
|
- name: "read -s with multiple variables"
|
|
stdin: |
|
|
echo "a b c" | { read -s v1 v2 v3; echo "v1='$v1' v2='$v2' v3='$v3'"; }
|
|
|
|
- name: "read into nameref variable"
|
|
known_failure: true
|
|
stdin: |
|
|
declare -n ref=result
|
|
read ref <<< "input_value"
|
|
echo "result: $result"
|
|
echo "ref: $ref"
|
|
|
|
- name: "read -a into nameref array"
|
|
known_failure: true
|
|
stdin: |
|
|
declare -n ref=myarr
|
|
read -a ref <<< "one two three"
|
|
echo "myarr[0]: ${myarr[0]}"
|
|
echo "myarr[1]: ${myarr[1]}"
|
|
echo "myarr[2]: ${myarr[2]}"
|
|
echo "ref[1]: ${ref[1]}"
|
|
|
|
- name: "read into subscripted nameref"
|
|
known_failure: true
|
|
stdin: |
|
|
arr=(a b c)
|
|
declare -n ref='arr[1]'
|
|
read ref <<< "replaced"
|
|
echo "arr[0]: ${arr[0]}"
|
|
echo "arr[1]: ${arr[1]}"
|
|
echo "arr[2]: ${arr[2]}"
|
|
|
|
- name: "read with multi-byte UTF-8 input"
|
|
stdin: |
|
|
printf '테스트 héllo\n' | { read -r x; echo "[$x]"; }
|
|
printf '\xed\x85\x8c b\n' | { read -r p q; echo "[$p][$q]"; }
|
|
|
|
- name: "read -d with a multi-byte delimiter"
|
|
stdin: |
|
|
printf 'ab\xc3\xa9c\n' | { read -r -d 'é' x; echo "[$x]"; }
|
|
|
|
# An invalid byte can't round-trip through the Rust String we read into, so these
|
|
# cases match on shape (`?` is one byte to the C-locale oracle, one character to us)
|
|
# rather than echoing the byte back.
|
|
- name: "read does not swallow bytes after an invalid UTF-8 byte"
|
|
stdin: |
|
|
# The 'Z' doesn't continue the sequence 0xC3 starts; it and 'b' must survive.
|
|
printf 'a\xc3Zb\n' | { read -r x; case "$x" in a?Zb) echo "match";; *) echo "NO: $x";; esac; }
|
|
# ...and when the byte that doesn't continue it is the delimiter, the line must
|
|
# still end there.
|
|
printf '\xc3\nSECOND\n' | { read -r a; read -r b; case "$a" in ?) echo "one";; *) echo "NO";; esac; echo "[$b]"; }
|
|
|
|
- name: "read with a truncated multi-byte sequence at EOF"
|
|
stdin: |
|
|
printf '\xe4\xb8' | {
|
|
read -r x; echo "rc=$?"
|
|
case "$x" in ??) echo "two";; *) echo "NO";; esac
|
|
read -r y; echo "rc2=$?"
|
|
}
|
|
|
|
- name: "read -n and -N count characters, not bytes"
|
|
# brush has no locale support and always decodes input as UTF-8, so the oracle
|
|
# needs a UTF-8 locale to agree; under the suite's default LC_ALL=C bash counts
|
|
# bytes instead.
|
|
env:
|
|
LC_ALL: "C.UTF-8"
|
|
stdin: |
|
|
printf '테스트\n' | { read -r -n 2 x; echo "[$x]"; }
|
|
printf '테스트\n' | { read -r -N 2 x; echo "[$x]"; }
|
|
printf 'héllo\n' | { read -r -n 3 x; echo "[$x]"; }
|
|
# 4-byte sequences.
|
|
printf '\xf0\x9f\x98\x80\xf0\x9f\x98\x80z\n' | { read -r -n 2 x; echo "[$x]"; }
|
|
printf '\xf0\x9f\x98\x80\xf0\x9f\x98\x80z\n' | { read -r -N 2 x; echo "[$x]"; }
|
|
# The limit is checked separately on the character after a backslash escape.
|
|
printf '\\\xc3\xa9\xed\x85\x8cx\n' | { read -n 2 x; echo "[$x]"; }
|
|
|
|
- name: "read into an invalid variable name fails"
|
|
known_failure: true # TODO(read): an invalid variable name is accepted
|
|
ignore_stderr: true
|
|
stdin: |
|
|
read 1x <<< "hello"
|
|
echo "read: $?"
|
|
|
|
- name: "read with an invalid timeout fails with status 1"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
read -t abc x <<< "hello"
|
|
echo "read: $?"
|
|
|
|
- name: "read takes control characters from a pipe as data"
|
|
known_failure: true # TODO(signals): control characters are dropped from any input
|
|
stdin: |
|
|
printf 'a\003b\004c\n' | { read -r x; echo "rc=$? len=${#x}"; }
|
|
|
|
- name: "read options given more than once keep the last"
|
|
stdin: |
|
|
read -n 1 -n 2 x <<< "abc"; echo "x=[$x] rc=$?"
|
|
read -d , -d . x <<< "a.b,c"; echo "x=[$x] rc=$?"
|
|
read -a a1 -a a2 <<< "x y"; declare -p a2; declare -p a1 2>/dev/null || echo "a1 unset"
|
|
read -t 5 -t 0 <<< "a"; echo "rc=$?"
|
|
|
|
- name: "read with combined and attached short options"
|
|
stdin: |
|
|
read -rn1 x <<< 'abc'; echo "x=[$x]"
|
|
read -n2 x <<< 'abc'; echo "x=[$x]"
|
|
read -rsn2 x <<< 'abc'; echo "x=[$x]"
|
|
read -rN2 x <<< $'a\nbc'; echo "x=[$x]"
|
|
read -d, x <<< 'a,b'; echo "x=[$x]"
|
|
read -dX x <<< 'aXb'; echo "x=[$x]"
|
|
read -ra arr <<< 'a\b c'; declare -p arr
|
|
read -aarr <<< 'a b'; declare -p arr
|
|
read -u0 x <<< 'fd0'; echo "x=[$x]"
|
|
read -n +3 x <<< 'abcdef'; echo "x=[$x]"
|
|
read -t .5 x <<< 'a'; echo "x=[$x] rc=$?"
|
|
|
|
- name: "read with -- before the names"
|
|
stdin: |
|
|
read -- x y <<< 'a b c'; echo "x=[$x] y=[$y]"
|
|
read -r -- x <<< 'a\b'; echo "x=[$x]"
|
|
|
|
- name: "read with an unknown option or missing value fails with status 2"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
read -Z x <<< 'a'; echo "rc=$?"
|
|
read -a; echo "rc=$?"
|
|
read -n; echo "rc=$?"
|
|
|
|
- name: "read with an invalid count or descriptor fails with status 1"
|
|
known_failure: true # TODO(read): fails with status 2
|
|
ignore_stderr: true
|
|
stdin: |
|
|
read -n abc x <<< 'a'; echo "rc=$?"
|
|
read -n -1 x <<< 'a'; echo "rc=$?"
|
|
read -N 1r x <<< 'a'; echo "rc=$?"
|
|
read -u abc x <<< 'a'; echo "rc=$?"
|
|
read -u -1 x <<< 'a'; echo "rc=$?"
|
|
|
|
- name: "read checks every -t and -u it is given"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
exec 99<&- # fd 99 must be closed, but the harness can leak one in
|
|
x=keep; read -t abc -t 1 x <<< data; echo "rc=$? x=$x"
|
|
x=keep; read -t inf -t 1 x <<< data; echo "rc=$? x=$x"
|
|
x=keep; read -t 1 -t abc x <<< data; echo "rc=$? x=$x"
|
|
x=keep; read -u 99 -u 0 x <<< data; echo "rc=$? x=$x"
|
|
x=keep; read -t 5 -t 1 x <<< data; echo "rc=$? x=$x"
|
|
|
|
- name: "read -t with an exponent fails with status 1"
|
|
known_failure: true # TODO(read): accepts 1e1
|
|
ignore_stderr: true
|
|
stdin: |
|
|
read -t 1e1 x <<< 'a'; echo "rc=$? x=[$x]"
|
|
|
|
- name: "read -n 0 and -N 0 read nothing"
|
|
known_failure: true # TODO(read): reads one character
|
|
stdin: |
|
|
read -n 0 x <<< 'abc'; echo "x=[$x] rc=$?"
|
|
read -N 0 x <<< 'abc'; echo "x=[$x] rc=$?"
|
|
|
|
- name: "read option values may begin with a hyphen"
|
|
known_failure: true # TODO(read): such a value is taken for an option
|
|
stdin: |
|
|
read -d -x x <<< 'a-xb'; echo "x=[$x] rc=$?"
|
|
read -d -- x <<< 'a-b'; echo "x=[$x] rc=$?"
|
|
read -p -x x <<< 'a'; echo "x=[$x] rc=$?"
|
|
|
|
- name: "read takes words after the first name as names"
|
|
known_failure: true # TODO(read): a later option-like word is taken for an option
|
|
ignore_stderr: true
|
|
stdin: |
|
|
read x -r <<< 'a b'; echo "x=[$x] rc=$?"
|
|
read x -- y <<< 'a b'; echo "x=[$x] y=[$y] rc=$?"
|
|
|
|
- name: "read -h is an invalid option and prints nothing on stdout"
|
|
stdin: |
|
|
x=$(read -h 2>/dev/null); echo "rc=$? len=${#x}"
|