6

In bash, this works:

echo -n $'a\nb\nc\n' | while read x; do echo = $x =; done

The while loops through three times

= a =
= b =
= c =

But imagine a text file that doesn't have the conventional trailing newline. I think that read should still work for all three lines, but it doesn't. I just get:

echo -n $'a\nb\nc' | while read x; do echo = $x =; done

= a =
= b =

The help read in bash doesn't really clarify.

Note: I don't need this resolved, and I can see some ways to fix it myself. I am curious, and I am tempted to file a bug report - I generally try myself to respect files that mightn't have the trailing new line. I came across this when using the -d option to read. read -d " " will split on spaces instead of newlines, but it will miss out on the last entry unless it has a trailing space.

(Ubuntu. GNU bash, version 4.1.5(1)-release)

codeforester
  • 34,080
  • 14
  • 96
  • 122
Aaron McDaid
  • 25,518
  • 9
  • 58
  • 85
  • for what it's worth, your 2nd script works as the first with ksh. Good luck. – shellter Dec 22 '11 at 22:06
  • I just checked: `ksh` behaves the same way as `bash` in this case, @shellter. – codeforester Jan 12 '21 at 00:22
  • 1
    @codeforester : I'm sure I tested this before posting, but no longer have access to the UWIN ksh93. Also it is possible that the version I was using is different that the version you are using, although I'm almost sure I saw this very issue come up in the UWIN/ksh email groups (~<10 yrs ago ;-) ). I like your alternate below. Cheers. – shellter Jan 12 '21 at 00:52

2 Answers2

6

If you want the above loop to process the incomplete line, do this:

echo -n $'a\nb\nc' | while read x || [[ $x ]]; do echo = $x =; done

which gives:

= a =
= b =
= c =

When read encounters the incomplete line, it does read that into the variable (x in this case) but returns a non-zero exit code which would end the loop, and || [[ $x ]] takes care of running the loop for the incomplete line as well. When read is called the next time, there is nothing to read and it exits with 1, setting x to an empty string as well, which ensures that we end the loop.


Related

codeforester
  • 34,080
  • 14
  • 96
  • 122
1
$ man bash
   read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]
          One line is read from the standard input, ...

I think the key is: How to define "One line".
Does text without a '\n' at the end makes One line?
I guess read don't think so.

kev
  • 146,428
  • 41
  • 264
  • 265
  • 1
    My text editor thinks it's a line, and I think every text editor will. So if the text editor can deal sensibly with files which don't have a newline as their very last character, then I think `read` should too :-) – Aaron McDaid Dec 22 '11 at 12:45