bug-findutils
[Top][All Lists]
Advanced

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

Re: How to suppress "terminated by signal 13"?


From: Bernhard Voelker
Subject: Re: How to suppress "terminated by signal 13"?
Date: Tue, 19 Jan 2021 02:32:38 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.6.0

On 1/15/21 2:47 AM, Peng Yu wrote:
> Hi,
> 
> I want to suppress the message "xargs: ls: terminated by signal 13".
> 
> $ find . -type f | xargs ls -altr | head
> ...
> xargs: ls: terminated by signal 13
> 
> I could use this. But I don't want to miss other error messages. Is
> there an option of xargs to just suppress this message? Thanks.
> 
> $ find . -type f | xargs ls -altr 2>/dev/null | head

Thanks for that question.

The quick answer is: no, xargs(1) does not have an option to control suppressing
the output of this error diagnostic.

The longer answer is a bit more complex.
First of all, the POSIX specification [1] requires this behavior:

[1] https://pubs.opengroup.org/onlinepubs/9699919799/utilities/xargs.html

  CONSEQUENCES OF ERRORS
  If [...] an invocation of the utility is terminated by a signal, [...],
  the xargs utility shall write a diagnostic message and exit without processing
  any remaining input.

Before going deeper, let me also mention the expected behavior of the write(2)
function [2]:

[2] https://pubs.opengroup.org/onlinepubs/007904875/functions/write.html

  ERRORS
  The write() and [...] functions shall fail if:
  ...
  [EPIPE]
    An attempt is made to write to a pipe or FIFO that is not open for
    reading by any process, or that only has one end open. A SIGPIPE signal
    shall also be sent to the thread.

Another good related reading is:
  https://bugs.gnu.org/34488 and
  https://bugs.gnu.org/11540

Your example does not show all aspects which I want to discuss, because
depending on the file hierarchy in '.' the list of arguments might already
fit into one invocation of 'ls'.  Also other errors might interfere.

Therefore, let's play the examples with the following:

* Use 'seq inf' as data producer which writes numbers to the xargs pipe 
infinitely.

* Use 'env printf "%s\n"' as the command to be executed by 'xargs -n5';
  this gives us each input item on a separate line in the output.

* Use 'head -n1' as the consumer which breaks the pipe after one line.


Now let's discuss.
In practice, these 3 things play together: SIGPIPE, EPIPE and exit code 255.

a) SIGPIPE:
With the default signal settings ("SIG_DFL"), a process will be terminated via 
the
SIGPIPE signal when it attempts to write() to a pipe where the (last) reader
has already disappeared.

As the utility has been terminated by a signal, xargs(1) will diagnose this
error and terminate itself.  No further processing.

  $ seq inf | xargs -n5 printf "%s\n" | head -n1
  1
  xargs: printf: terminated by signal 13


b) EPIPE:
When the signal SIGPIPE is ignored ("SIG_IGN"), the utility attempting to
write() to a closed pipe will not receive the SIGPIPE signal, and instead
the write() syscall will return with an error and errno=EPIPE.

Usually, a well-written utility will know that further outputting does not
make sense, and therefore will output an EPIPE error diagnostic, and then
terminate itself with the exist status 1.

Now, xargs(1) notices that the utility ended with an error, but as the
exit code was not 255, it will continue to process the input and launch
the utility as often as needed.  Well, if the work of the utility is to
filter its input and write() it to the same already closed output pipe,
all those invocations will also fail like the first one.

In our example, we'll see this an an endless loop until we press CTLR+C:

  $ seq inf | ( trap '' PIPE && xargs -n5 printf "%s\n" ) | head -n1
  1
  printf: write error: Broken pipe
  printf: write error: Broken pipe
  ...
  printf: write error: Broken pipe
  printf: write error: Broken pipe
  ^C

c) exit code 255:
xargs only terminates processing further input if the command being run
terminated with an exist code of 255 (or was killed by a signal, as in a)).

This means: in an environment where SIGPIPE is ignored, and where the xargs(1)
processing needs to be stopped after an error, the COMMAND has to exit with
status 255.

  $ seq inf \
     | xargs -n5 sh -c '
         trap "" PIPE \
           && printf "%s\n" "$@" \
           || exit 255' sh \
     | head -n1
  1
  sh: line 0: printf: write error: Broken pipe
  xargs: sh: exited with status 255; aborting

As can be seen, xargs(1) will still diagnose the exit status 255
(even if the command being run would swallow the EPIPE message
"write error: Broken pipe").


What does all that mean?

1. With today's xargs(1), the processing will continue after a EPIPE
error when SIGPIPE is ignored.

Well, xargs(1) could ensure that SIGPIPE is always set to the default
action in the child process after the fork():
      signal (SIGPIPE, SIG_DFL);
By that, case b) would be changed to terminate the processing.
OTOH, the caller may have set SIGPIPE to SIG_IGN by intention.

Furthermore, the COMMAND being called may write to several files or pipes,
and only one - e.g. a logger - may be closed, and further invocation are
desired/expected and would maybe succeed again.
Therefore, adding the above signal() call hard-coded would be definitely
wrong.

2. With today's xargs(1), it is not possible to suppress the SIGPIPE
message (without discarding all the other possible errors to /dev/null
as well).

The attached is a first draft to add the option --sigpipe-nowarn; with this,
the use can control whether xargs(1) diagnoses the SIGPIPE error or not.
Please note that this option implicitly sets the SIGPIPE handler to SIG_DFL
in the child process, although the invoking shell might have set it to
SIG_IGN.

I might have overseen something - or even I'm totally off.
Comments welcome.

Have a nice day,
Berny

Attachment: xargs-sigpipe-nowarn.diff
Description: Text Data


reply via email to

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