head -n 1
, when given an empty stream on stdin, is well within its rights and specification to immediately exit with a successful exit status.
Thus:
seq 2 | while head -n 1 ; do : ; done
...can legally loop forever, as head -n 1
is not required to exit with a nonzero status and thus terminate the loop. (A nonzero exit status is only required by the standard if "an error occurred", and a file having fewer lines than are requested for output is not defined as an error).
Indeed, this is explicit:
When a file contains less than number lines, it shall be copied to standard output in its entirety. This shall not be an error.
Now, if your implementation of head
, after its first invocation, (printing the contents of the first line), leaves the file pointer queued up at the beginning of the second line when it exits, (which it is absolutely not required to do), then the second loop instance will then read that second line and emit it. Again, however, this is an implementation detail which depends on whether the folks writing your head
implementation chose to either:
- Read an aggressively large block, but only emit a subset of it. (The more efficient implementation.)
- Or loop character-by-character to only consume a single line.
An implementer is well within their rights to decide which of those implementations to follow based on criteria only available at runtime.
Now, let's say your head
always tries to read 8kb blocks at a time. How, then, could it ever leave the pointer queued up for the second line? [* - other than seeking backwards, which some implementations do when given a file, but which is not required by the standard; thanks to Rob Mayhoff for the pointer here]
This can happen if the concurrent invocation of seq
has only written and flushed a single line as of when the first read
occurs.
Obviously, it's a very timing-sensitive situation -- a race condition -- and also depends on unspecified implementation details, (whether seq
flushes its output between lines -- which, as seq
is not specified as part of POSIX or any other standard, is completely variant between platforms).