Repository navigation
Description
Observed behaviour
When read reads N variables from a string that holds N+1 variables if split by $IFS, variable number N will contain excess whitespace if more than one blank is separating the words.
E.g.: After Reading the string "A<space><tab><space>B<space><tab><space>C" into variables $A and $REST, $REST will contain "<tab><space>B<space><tab><space>C".
Expected behaviour
After Reading the string "A<space><tab><space>B<space><tab><space>C" into variables $A and $REST, $REST should contain "B<space><tab><space>C".
Relevant parts of read(1) says:
If no option to determine how to split like --delimiter, --line or
--tokenize is given, the variable IFS is used as a list of characters
to split on. Relying on the use of IFS is deprecated and this behaviour
will be removed in future versions.
and
If there are more tokens than variables, the complete remainder is
assigned to the last variable.
While I can see how the βthe use of IFS is deprecatedβ and βthe complete remainderβ can be interpreted in more than one way, skipping all blanks and only putting "word<blank(s)>word" in the last variable is consistent with how a number of other shells β notably bash, dash, ksh, mksh, posh, sh, yash and zsh β are splitting a string into a number of words, and if fish's behaviour is intentional, I fail to see the logic or usefulness.
Examples
fish
$ cat read-test.fish
#!/bin/sh
# run with fish
# A<space><tab><space>B<space><tab><space>C
set TEST (printf 'A \t B \t C')
printf 'TEST="%s"\n' "$TEST"
# Assert that length of TEST is 1+3+1+3+1=9
printf 'len(TEST)=%s\n' (string length "$TEST")
# Let's read A into variable A and the rest into variable REST
echo "$TEST" \
| while read A REST
printf 'A="%s"\n' "$A"
printf 'REST="%s"\n' "$REST"
printf 'len(REST)=%s\n' (string length "$REST")
end
:
# eof
$ fish read-test.fish
TEST="A B C"
len(TEST)=9
A="A"
REST=" B C"
len(REST)=7bash et al.
$ cat read-test.sh
#!/bin/sh
# run with bash, dash, ksh, mksh, posh, sh, yash, zsh
# A<space><tab><space>B<space><tab><space>C
TEST=$(printf 'A \t B \t C')
printf 'TEST="%s"\n' "$TEST"
# Assert that length of TEST is 1+3+1+3+1=9
printf 'len(TEST)=%s\n' "${#TEST}"
# Let's read A into variable A and the rest into variable REST
echo "$TEST" \
| while read A REST
do
printf 'A="%s"\n' "$A"
printf 'REST="%s"\n' "$REST"
printf 'len(REST)=%s\n' "${#REST}"
done
:
# eof
$ bash read-test.sh
TEST="A B C"
len(TEST)=9
A="A"
REST="B C"
len(REST)=5Version(s)
$ fish --version
fish, version 3.0.2-1939-gea7868988
$ uname -a
Linux brainfreeze 5.4.2-arch1-1 #1 SMP PREEMPT Thu, 05 Dec 2019 12:29:40 +0000 x86_64 GNU/Linux
$ echo $TERM
xterm-256color- GNU bash, version 5.0.11(1)-release (x86_64-pc-linux-gnu)
- dash 0.5.10.2
- ksh 2020.0.0
- mksh 57
- posh 0.13.2
- yash 2.49
- zsh 5.7.1
π read-test.fish
π read-test.sh
Activity
faho commentedon Dec 10, 2019
So, simplified:
Yeah, that doesn't remove the last chain of separators. It probably should. It's behaved like that since at least 3.0.2.
kseistrup commentedon Dec 10, 2019
Yes, simplified, but I wanted to show scripts that were similar for both fish and bash, and the latter will spawn a subshell, so the
echo | readwon't work.It was like that even in the v2 series, and since it is also like that in v3, I thought perhaps it was a feature. But it doesn't make any sense as a feature, so here we are.
completions/git: Work around read trimming whitespace
completions/git: Work around read trimming whitespace
krobelus commentedon Feb 29, 2020
reverted in 0c74ff4
zanchey commentedon Apr 19, 2020
So this got reverted because it broke some existing code, but I wonder if it should be reintroduced under a feature flag as it seems to be the more correct option.
kseistrup commentedon Apr 19, 2020
But now nobody will notice since the issue is closed.
krobelus commentedon Apr 22, 2020
It's hard to tell what's the best behavior here is; in
while read lineit's probably the current one, see also #6650 (comment), but inread a b cit's the opposite.