Skip to content

β€˜read’ includes blanks in variable when it shouldn'tΒ #6406

Open

Description

@kseistrup

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)=7

bash 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)=5

Version(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

  1. added
    bugSomething that's not working as intended
    on Dec 10, 2019
  2. added this to the fish-future milestone on Dec 10, 2019
  3. faho commented on Dec 10, 2019

    @faho
    Member

    So, simplified:

    $ echo "a    b     c" | read a b c
    $ string escape $a $b $c
    a
    b
    '    c'

    Yeah, that doesn't remove the last chain of separators. It probably should. It's behaved like that since at least 3.0.2.

  4. kseistrup commented on Dec 10, 2019

    @kseistrup
    ContributorAuthor

    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 | read won'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.

  5. krobelus commented on Feb 29, 2020

    @krobelus
    Contributor

    reverted in 0c74ff4

  6. zanchey commented on Apr 19, 2020

    @zanchey
    Member

    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.

  7. kseistrup commented on Apr 19, 2020

    @kseistrup
    ContributorAuthor

    But now nobody will notice since the issue is closed.

  8. krobelus commented on Apr 22, 2020

    @krobelus
    Contributor

    It's hard to tell what's the best behavior here is; in while read line it's probably the current one, see also #6650 (comment), but in read a b c it's the opposite.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething that's not working as intended

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions