Discussion:
shell read built-in
Eugene Grosbein
2018-10-22 12:08:58 UTC
Permalink
Hi!

$ echo test | read line; echo $?
0
$ echo -n test | read line; echo $?
1

Is it right behavior for /bin/sh?
This makes it hard for "while read line ... < file"
to process last line if it is not followed by new line character.

Eugene
Yuri Pankov
2018-10-22 12:30:40 UTC
Permalink
Post by Eugene Grosbein
Hi!
$ echo test | read line; echo $?
0
$ echo -n test | read line; echo $?
1
Is it right behavior for /bin/sh?
This makes it hard for "while read line ... < file"
to process last line if it is not followed by new line character.
POSIX defines the exit status for read as (and sh(1) says the same):

The following exit values shall be returned:
0 Successful completion.
Post by Eugene Grosbein
0 End-of-file was detected or an error occurred.
...and looks like all of /bin/sh, bash, ksh93 seem to agree on the
behavior here.
Yuri Pankov
2018-10-22 13:32:02 UTC
Permalink
Post by Yuri Pankov
Post by Eugene Grosbein
Hi!
$ echo test | read line; echo $?
0
$ echo -n test | read line; echo $?
1
Is it right behavior for /bin/sh?
This makes it hard for "while read line ... < file"
to process last line if it is not followed by new line character.
0 Successful completion.
Post by Eugene Grosbein
0 End-of-file was detected or an error occurred.
...and looks like all of /bin/sh, bash, ksh93 seem to agree on the
behavior here.
BTW, it looks like last line is still parsed despite not having \n, so
you could workaround it using something like (yes, looks ugly):

$ printf "foo bar\n" | (while read a b; do printf "%s %s\n" $a $b; done;
if test -n "$a$b"; then printf "%s %s\n" $a $b; fi)
foo bar
$ printf "foo bar" | (while read a b; do printf "%s %s\n" $a $b; done;
if test -n "$a$b"; then printf "%s %s\n" $a $b; fi)
foo bar
Martin Beran
2018-10-22 14:35:11 UTC
Permalink
Post by Yuri Pankov
BTW, it looks like last line is still parsed despite not having \n, so
$ printf "foo bar\n" | (while read a b; do printf "%s %s\n" $a $b; done;
if test -n "$a$b"; then printf "%s %s\n" $a $b; fi)
foo bar
$ printf "foo bar" | (while read a b; do printf "%s %s\n" $a $b; done;
if test -n "$a$b"; then printf "%s %s\n" $a $b; fi)
foo bar
If code in the while loop is more complex and you do not want to repeat
it twice or define a shell function for it, you can use:

while { read l; e=$?; [ $e = 0 ]; } || [ -n "$l" ]; do
: any code that processes $l
[ $e = 0 ] || break
done;

The condition tests that either read returns 0 (a line terminated by
'\n') or puts a nonempty value to $l (the last line not terminated by
'\n'). The break command eliminates further read after EOF (nonzero
return), because on a terminal, after EOF (pressing ^D), read returns
false once. When called again, it continues reading from the terminal
until the next ^D.
--
Martin Beran
Loading...