You'd need a command that reads two lines and no more than two lines.
To be able to do that when the input is a pipe which is not seekable, commands have three choices:
- read one byte at a time until they finds the second newline character.
- peek at (without consuming) the contents of the pipe to see where that second newline is and then read as much data in one go.
- read input in blocks, but if they find they have read more than the two lines, put back what they've read too much onto the pipe.
1 is expensive on many systems as that's one read()
system call per byte. 2 and 3 don't work on all systems as not all systems support peeking at pipe or put back data onto the pipe and that's also not reliable when more than one process reads from the pipe at the same time.
So in general, except for a few exceptions, commands generally don't bother reading exactly how many lines they're being told to when their input is not seekable.
With most implementations of head
, head -n2
will output the first two lines of its input, but may consume more input. A notable exception is the head
builtin of the ksh93 shell which will peek if it can and read one byte at a time otherwise.
There are commands that can be told to make sure they don't read past the last line they're told to process.
With GNU sed
, that's with the -u
option:
sed -u 2q
will read 2 lines (1 byte at a time) and no more.
The line
utility (no longer a standard one, so not always found these days) is one that reads exactly one line.
The read
builtin of sh
is required not to read more than one line. So that line
command above can be implemented with:
IFS= read -r line; printf '%s\n' "$line"
(with the caveat that it adds a newline character even if the input line wasn't terminated).
(POSIX actually justified removing line
on the basis that it could be achieved with read
).
For a line
implementation that doesn't add extra newlines and return false on eof.
line() {
if IFS= read -r line; then
printf '%s\n' "$line"
else
printf %s "$line"
false
fi
}
And twolines
is just:
twolines() { line && line; }
And then you can do:
mkfifo pipe
ls > pipe &
while
echo next two lines:
twolines
do
continue
done < pipe
read
. – AlexP Oct 22 '19 at 21:37