3

I've been revising the IFS stuff around here and there and could in no way explain the behaviour of different code snippets that boils down to this:

$ read -r < <(printf "%s\n" " x ")  && echo \<"$REPLY"\>
< x >
$ read -r line < <(printf "%s\n" " x ")  && echo \<"$line"\>
<x>

Of course man bash has it all.

If no names are supplied, the line read is assigned to the variable REPLY. The return code is zero, unless end-of-file is encountered, read times out (in which case the return code is greater than 128), a variable assignment error (such as assigning to a readonly variable) occurs, or an invalid file descriptor is supplied as the argument to -u.

I just thought it would be nice if one of those IFS-related answers contained a word on this casus. Some are even openly wrong, as it turns out. One example is the edit in this paragraph from this answer:

IFS is not used by read unless it is called with arguments. (Edit: This is not exactly true: whitespace characters, i.e. space and tab, present in IFS are always ignored at the beginning/end of the input line. )

This is a rhetorical question with no question mark. But correct me if I'm wrong, etc.

1
  • It seems that zsh returns <x> in both cases. Commented Jul 31, 2017 at 19:09

1 Answer 1

3

The normal behavior, followed by all Bourne/POSIX shells (dash, ksh, pdksh, mksh, bash, zsh even when not in sh emulation mode, BusyBox sh, Bourne shell, …) is that read -r line strips leading and trailing whitespace characters. More generally, read -r line strips a leading separator which consists of an arbitrary number of whitespace characters from IFS and at most one non-whitespace character from IFS. At the end of the line, only whitespace characters can get stripped, because if the line ends with a non-whitespace separator character then it ends with an empty field. This is a special case of the more general case: the first variable gets the first field, the second variable gets the second field, etc., and if there are more fields than variables then the last variables gets the remainder of the line except the trailing whitespace.

Calling read with an implicit variable name is a ksh extension which bash and zsh imitate. In ksh (both ATT and pdksh/mksh) and in zsh, read without a variable name is exactly equivalent to read with the variable name REPLY. In bash, for some reason, the behavior is different: when the variable name is implicit, IFS whitespace strippping is not done. This is deliberate, but the rationale for making it different escapes me. (If you want no stripping, the standard method is to run IFS= read -r.)

  /* If there are no variables, save the text of the line read to the
     variable $REPLY.  ksh93 strips leading and trailing IFS whitespace,
     so that `read x ; echo "$x"' and `read ; echo "$REPLY"' behave the
     same way, but I believe that the difference in behaviors is useful
     enough to not do it.  Without the bash behavior, there is no way
     to read a line completely without interpretation or modification
     unless you mess with $IFS (e.g., setting it to the empty string).
     If you disagree, change the occurrences of `#if 0' to `#if 1' below. */
  if (list == 0)
    {
#if 0
      orig_input_string = input_string;
      for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && isifs(*t
        ;
      input_string = t;
      input_string = strip_trailing_ifs_whitespace (input_string, ifs_chars, saw
#endif

You must log in to answer this question.