coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Does head util cause SIGPIPE?


From: Bernhard Voelker
Subject: Re: Does head util cause SIGPIPE?
Date: Sat, 26 Oct 2019 00:59:20 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.1.0

[adding the findutils ML]

On 2019-10-25 09:56, Ray Satiro wrote:
> Recently I tracked down a delay in some scripts to this line:
> 
> find / -name filename* 2>/dev/null | head -n 1
> 
> It appeared that the searching continued after the first result was 
> found. I expected head would terminate and SIGPIPE would be sent which 
> would cause find to terminate, but that did not happen. Since I was in 
> cygwin I thought maybe it was a Windows issue but I tried in Ubuntu 16 
> with an aribtrary file and noticed the same thing. I also tried 
> disabling any pipe trap and monitoring with strace. It looks like head 
> exits but find continues searching. This was only observable the first 
> run since some caching seems to be done which makes subsequent runs 
> complete too fast to tell, but if I reset the VM I can reproduce.
> 
> owner@ubuntu1604-x64-vm:~$ ( trap '' pipe; find / -name initrd* 
> 2>/dev/null | strace -e 'trace=!all' head -n 1)
> /initrd.img
> +++ exited with 0 +++
> (few seconds wait)

'head' just terminates after it has read the given number of lines.

> Since we only need the first line I can just use find options -print 
> -quit and skip piping to head. But say we needed the first n results, 
> how would I do that with head and get find to terminate rather than 
> continue searching?
> 
> find (GNU findutils) 4.7.0-git
> head (GNU coreutils) 8.25

... and 'find' properly terminates after receiving EPIPE:

  $ strace -ve write find /dev | head -n 1 >/dev/null
  write(1, "/dev\n/dev/.blkid.tab\n/dev/.blkid"..., 4096) = 4096
  write(1, "chi_HDS721010CLA330_JP2911N03AR0"..., 4096) = -1 EPIPE (Broken pipe)
  --- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=21014, si_uid=1000} ---
  +++ killed by SIGPIPE +++

As you can see from above strace output, find does some buffering of the
output, i.e., it does not write one line per entry.
Thus, I assume that searching through the files until the first 4k output
buffer is full takes a while on your system.

To confirm, please try with turning this buffering off with 'stdbuf':

  $ trace -vfe write stdbuf -o 0 find /dev | head -n 1 >/dev/null
  write(1, "/dev\n", 5)                   = 5
  write(1, "/dev/.blkid.tab\n", 16)       = -1 EPIPE (Broken pipe)
  --- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=21098, si_uid=1000} ---
  +++ killed by SIGPIPE +++

With more depth, another kind of buffering comes into play:
'find' uses gnulib's FTS module to read the next directory hierarchy.
This means, that although 'head' might terminate at e.g. 10 items,
'find' already has read the entries for a couple of entries more.

Finally, the situation becomes more complicated if it is not 'find' which
is writing into the broken pipe, but a -exec'ed tool: in that case,
that tool terminates due to EPIPE but find will continue processing
with the next entry:

  $ mkdir x && cd x
  $ seq 10 | xargs touch
  $ find . -exec echo '{}' \; | head -n 3
  .
  ./10
  ./9
  find: 'echo' terminated by signal 13
  find: 'echo' terminated by signal 13
  find: 'echo' terminated by signal 13
  find: 'echo' terminated by signal 13
  find: 'echo' terminated by signal 13
  find: 'echo' terminated by signal 13
  find: 'echo' terminated by signal 13
  find: 'echo' terminated by signal 13

FWIW: To work around the above, one would use the false value of -exec in the
case the command to be run failed:

  $ find . -exec echo '{}' \; -o -quit | head -n 3
  .
  ./10
  ./9
  find: 'echo' terminated by signal 13

Does this describe the effects you see?

Have a nice day,
Berny



reply via email to

[Prev in Thread] Current Thread [Next in Thread]