Re: sh read on files missing the final newline

Robert Elz <[email protected]> Wed, 08 Jul 2026 04:55:16 +0700
Newsgroups gmane.os.netbsd.devel.userlevel
Message-ID <[email protected]>
    Date:        Tue, 7 Jul 2026 15:38:47 +0200
    From:        Edgar Fu=DF <ef=40math.uni-bonn.de>
    Message-ID:  <ak0BZ0Nw0OOnfbpD=40trav.math.uni-bonn.de>

  =7C Are you talking about NetBSD's /bin/sh
  =7C or what a POSIX shell is required to do?

Both.

  =7C I also don't understand wat you mean by =22back in SUS days=22.

The change to add different delimiters than =5Cn is more recent than SUS
(I think it appeared in Posix 7 (would be 2008 probably, but could perhap=
s
be one of its updates, 2013, 2016, 2018?) but I haven't verified that).

Before then, all read records were terminated by =5Cn, which made them be=

lines.

  =7C So, are you saying that a POSIX-compliant shell's read command is r=
equired=20
  =7C to read (and split into the arg vars) a trailing =22incomplete line=
=22?

Yes.  An incomplete logical line to be more precise.

  =7C If yes, if I substitute the usual
  =7C 	while read foo bar baz; do
  =7C 		...
  =7C 	done
  =7C with
  =7C 	while read foo bar baz =7C=7C =5B -n =22=24foo=22 =5D; do
  =7C 		...
  =7C 	done
  =7C would that correctly deal with =22incomplete lines=22 for a POSIX
  =7C compliant shell?

Perhaps.   There is no really correct way to handle it, as =22foo=22 migh=
t
end up being the empty string for other reasons than reaching EOF with
an ending delimiter on the previous record (and even on the line with no
delimiter).  So might all of bar baz (and however many other variables
you have).

If, for example, IFS=3D: (and we leave the end delim as =5Cn for simplici=
ty)
then
	::
would generate foo=3D'' bar=3D'' baz=3D'' regardless of whether that seco=
nd
':' is followed by =5Cn or by EOF.   So there simply is no reliable way
to know for sure if an incomplete line had data on it or not.

But if your data makes that kind of thing impossible (all the fields
are not permitted to be empty, or something), a technique like
that might work, but I would prefer to code it more like:

	X=3D0
	while =5B =22=24X=22 -eq 0 =5D && =7B
		=7B read foo bar baz; X=3D=24?; =5B =22=24X=22 -le 1 =5D && =7B
		  =5B =22=24X=22 -eq 0 =5D =7C=7C =5B -n =22=24foo=24bar=24baz=22 =5D ;=
 =7D =7D
	do
		...
	done

in order to avoid doing another read after one has returned 1, and treati=
ng
a status of >1 as always being fatal (that would happen if we had had
	readonly foo
prior to this, or similar, could also happen on a read I/O error).

Avoiding another read is less important if the data is coming from a pipe=

or file, but if it is from the terminal, it would simply go and read agai=
n
and wait for the next EOF to appear otherwise.

It is incorrect to test the var values if read's exit status is >1, what
(if anything) they are set to in that case is unspecified (they're very
likely to just be whatever they were before the read command, but could
be unset, or some could have new values, and others be left alone or unse=
t,
or just about anything.)

kre

ps: don't blindly trust that final piece of code, I think I got the logic=

and syntax right (there are certainly other ways to rewrite it which woul=
d
be just as correct), but it has only ever existed in this e-mail=21  I'm =
sure
you can correct it if I messed it up however.   Needless to say, don't al=
ter
X in the =22...=22 and when the loop ends, =24? will (as always) be the e=
xit status
of the last =22...=22 command executed (if any, 0 otherwise) and =24X wil=
l be the
exit status of the final read executed.