Subject: Re: bug#33281: head does not consume input after '-c' is satisfied

From: Bernhard Voelker <[email-masked]>
Date: Tue, 6 Nov 2018 08:06:38 +0100

On 11/5/18 10:17 PM, Philip Rowlands wrote:
> On Mon, 5 Nov 2018, at 20:30, Luiz Angelo Daros de Luca wrote:
>>
>> Once head read enough bytes to satisfy -c option, it stops reading input
>> and quit.
>> This is different from what -n does and it is also different from both
>> FreeBSD and busybox head implementation.
>>
>> With GNU Coreutils head:
>>
>> $ echo -e "123\n456\n789" | { head -n 1; while read a; do echo "-$a-";
>> done; }
>> 123
> 
> This is incomplete; head doesn't read everything, but more than one line. On my (rather aged Linux) system:
> $ head --version
> head (GNU coreutils) 8.25
> 
> $ seq 1864 | { head -n 1; while read a; do echo "-$a-"; done; }
> 1
> --
> -1861-
> -1862-
> -1863-
> -1864-
> 
> What's special about 1860 lines of output? It's just over the amount of data which head reads from the pipe, 8192 bytes.

Indeed, running 'head' via 'strace' seconds that:

  read(0, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14"..., 8192) = 8192

... and: 'head' tries to "undo" the reading by calling lseek(),
but that typically fails as stdin is a pipe:

  lseek(0, -8190, SEEK_CUR)               = -1 ESPIPE (Illegal seek)

Thus said, if your input was a regular file, then this positioning back to
where the newline "\n" was would succeed:

  $ file=$(mktemp) \
      && seq 4 > "$file" \
      && { strace -ve read,lseek head -n 1; while read a; do echo "-$a-"; done; } < "$file" \
      ; rm -f "$file"
  ...
  read(0, "1\n2\n3\n4\n", 8192)           = 8
  lseek(0, -6, SEEK_CUR)                  = 2
  1
  +++ exited with 0 +++
  -2-
  -3-
  -4-

Have a nice day,
Berny