Date: Tue, 7 Jul 2026 15:38:47 +0200
From: Edgar Fu� <[email protected]>
Message-ID: <[email protected]>
| Are you talking about NetBSD's /bin/sh
| or what a POSIX shell is required to do?
Both.
| I also don't understand wat you mean by "back in SUS days".
The change to add different delimiters than \n is more recent than SUS
(I think it appeared in Posix 7 (would be 2008 probably, but could perhaps
be one of its updates, 2013, 2016, 2018?) but I haven't verified that).
Before then, all read records were terminated by \n, which made them be
lines.
| So, are you saying that a POSIX-compliant shell's read command is required
| to read (and split into the arg vars) a trailing "incomplete line"?
Yes. An incomplete logical line to be more precise.
| If yes, if I substitute the usual
| while read foo bar baz; do
| ...
| done
| with
| while read foo bar baz || [ -n "$foo" ]; do
| ...
| done
| would that correctly deal with "incomplete lines" for a POSIX
| compliant shell?
Perhaps. There is no really correct way to handle it, as "foo" might
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=: (and we leave the end delim as \n for simplicity)
then
::
would generate foo='' bar='' baz='' regardless of whether that second
':' is followed by \n 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=0
while [ "$X" -eq 0 ] && {
{ read foo bar baz; X=$?; [ "$X" -le 1 ] && {
[ "$X" -eq 0 ] || [ -n "$foo$bar$baz" ] ; } }
do
...
done
in order to avoid doing another read after one has returned 1, and treating
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 again
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 unset,
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 would
be just as correct), but it has only ever existed in this e-mail! I'm sure
you can correct it if I messed it up however. Needless to say, don't alter
X in the "..." and when the loop ends, $? will (as always) be the exit status
of the last "..." command executed (if any, 0 otherwise) and $X will be the
exit status of the final read executed.