59

I'm using the following command to delete four largest size files in a folder:

find "/var/www/site1/" -maxdepth 1 -type f | xargs ls -1S | head -n 4 | xargs -d '\n' rm -f 

It works fine, but from time to time throws broken pipe error:

xargs: ls: terminated by signal 13 
2
  • You can add: -print | uniq | xargs ls -1S...... Commented Jun 3, 2018 at 10:05
  • Have a look at this answer for a reasonably clean way to solve the problem: unix.stackexchange.com/a/558018/264963 basically put an -exec read \; in the find command and give it as many imput lines as you want it to handle. Commented May 3, 2023 at 9:34

5 Answers 5

58

I ran across a similar issue and found this thread on search for an answer:

Signal 13 means something is written to a pipe where nothing is read from anymore (e.g. see http://people.cs.pitt.edu/~alanjawi/cs449/code/shell/UnixSignals.htm ).

The point here is that the ls command as executed by xargs is still writing output when the following head command already got all the input it wants and closed its input-pipe. Thus it's safe to ignore, yet it's ugly. See also the accepted answer in https://superuser.com/questions/554855/how-can-i-fix-a-broken-pipe-error

Sign up to request clarification or add additional context in comments.

Comments

20

You are purposely terminating your program with head -n 4, which creates the broken pipe because you terminated it before the "caller" finished. Since this is expected by you, you can ignore the error by redirecting it to /dev/null which discards it:

find "/var/www/site1/" -maxdepth 1 -type f | xargs ls -1S | head -n 4 | xargs -d '\n' rm -f 2>/dev/null

1 Comment

I'd rather redirect the "offending xargs": <cmd> | xargs ls -1S 2>/dev/null | head | <cmd>
4

I got the same error, "terminated by signal 13", under different circumstances and other answers here helped me work out the fix. I'd like to expand on the nature of the problem:

corpy386 ~/gw/Release/5.1_v9/ClaimCenter $ find . -name '*.pcf' -not -name '*build*' | xargs grep -l ClaimSnapshotGeneralPanelSet | ( read f && echo $f && grep 'def=' $f ) ./modules/configuration/build/idea/classes/web/pcf/claim/snapshot/default/ClaimSnapshotGeneralPanelSet.auto.pcf def="AddressSnapshotInputSet(Snapshot.LossLocation, Snapshot)" xargs: grep: terminated by signal 13 

So here's the same error and I'd only get a single line of output when I knew there are numerous files that match what I'm looking for. The problem was that xargs is producing multiple lines of output and read is only consuming a single line before ending. xargs tries to write the rest of its results to one of the pipes but the receiving end has already quit and gone home. Hence, signal 13: Broken Pipe.

The fix was to consume all of xargs's output by looping - change read f && do_some_things (which reads one time only) to while read f; do do_some_things; done.

corpy386 ~/gw/Release/5.1_v9/ClaimCenter $ **find . -name '*.pcf' -not -name '*build*' | xargs grep -l ClaimSnapshotGeneralPanelSet | while read f; do echo $f; grep 'def=' $f; done** ./modules/configuration/build/idea/classes/web/pcf/claim/snapshot/default/ClaimSnapshotGeneralPanelSet.auto.pcf def="AddressSnapshotInputSet(Snapshot.LossLocation, Snapshot)" ./modules/configuration/build/idea/classes/web/pcf/claim/snapshot/default/ClaimSnapshotGeneralPanelSet.gl.pcf def="AddressSnapshotInputSet(Snapshot.LossLocation, Snapshot)" ./modules/configuration/build/idea/classes/web/pcf/claim/snapshot/default/ClaimSnapshotGeneralPanelSet.Pr.pcf def="ClaimSnapshotGeneralPRPanelSet(Claim, Snapshot)" ./modules/configuration/build/idea/classes/web/pcf/claim/snapshot/default/ClaimSnapshotGeneralPanelSet.Trav.pcf def="AddressSnapshotInputSet(Snapshot.LossLocation, Snapshot)" ./modules/configuration/build/idea/classes/web/pcf/claim/snapshot/default/ClaimSnapshotGeneralPanelSet.wc.pcf def="AddressSnapshotInputSet(Snapshot.LossLocation, Snapshot)" ./modules/configuration/build/idea/classes/web/pcf/claim/snapshot/default/ClaimSnapshotLossDetailsScreen.default.pcf def="ClaimSnapshotGeneralPanelSet(Claim, SnapshotParam)" ./modules/configuration/config/web/pcf/claim/snapshot/default/ClaimSnapshotGeneralPanelSet.auto.pcf def="AddressSnapshotInputSet(Snapshot.LossLocation, Snapshot)" ./modules/configuration/config/web/pcf/claim/snapshot/default/ClaimSnapshotGeneralPanelSet.gl.pcf def="AddressSnapshotInputSet(Snapshot.LossLocation, Snapshot)" ./modules/configuration/config/web/pcf/claim/snapshot/default/ClaimSnapshotGeneralPanelSet.Pr.pcf def="ClaimSnapshotGeneralPRPanelSet(Claim, Snapshot)" ./modules/configuration/config/web/pcf/claim/snapshot/default/ClaimSnapshotGeneralPanelSet.Trav.pcf def="AddressSnapshotInputSet(Snapshot.LossLocation, Snapshot)" ./modules/configuration/config/web/pcf/claim/snapshot/default/ClaimSnapshotGeneralPanelSet.wc.pcf def="AddressSnapshotInputSet(Snapshot.LossLocation, Snapshot)" ./modules/configuration/config/web/pcf/claim/snapshot/default/ClaimSnapshotLossDetailsScreen.default.pcf def="ClaimSnapshotGeneralPanelSet(Claim, SnapshotParam)" 

This isn't exactly the same situation as OP's script - They wanted a part of the input and cut it off on purpose, I wanted the whole stream and cut it off by accident - but the shell semantics work out the same. Programs tend to be written to keep running until they have consumed all their input rather than test to see if their recipient is still listening.

1 Comment

Yes I know I got the options to find wrong - should have used 'path' instead of 'name' to filter out directories. I've left the commands as-is for the sake of illustration, since fixing the predicate made only one file come back in the first version, which meant the broken-pipe situation didn't even occur.
2

One way to avoid this is to switch head for perl -ne.

Starting with your original command:

find "/var/www/site1/" -maxdepth 1 -type f | xargs ls -1S | head -n 4 | xargs -d '\n' rm -f 

As you've probably learned from the other answers, the head -n 4 process exits after accepting 4 lines from ls -1S. This confuses ls -1S.

Replace head -n 4 with perl -ne 'print if $i++ <= 4'. The perl process will stay alive and accept every line from ls -1S, so no more error. But it will only pass along lines 1 - 4.

Here's the new command:

find "/var/www/site1/" -maxdepth 1 -type f | xargs ls -1S | perl -ne 'print if $i++ <= 4' | xargs -d '\n' rm -f 

https://perldoc.perl.org/perlrun#-n

Comments

0

I suggest to find and destroy "xargs" when "head" job is done

$ cat /tmp/50m.txt | xargs -n1 echo | { head -n2; kill -2 $(pidof xargs); }; echo "${PIPESTATUS[*]}" 141 130 0 

130 return code shows that "xargs" got signal 2 (SIGINT) and was killed by its signal handler silently.

141 return code shows that "cat" got signal 13 (SIGPIPE) and was killed by its signal handler silently in comparison with "xargs" that dies with the message "xargs: echo: terminated by signal 13" when it gets SIGPIPE

Usefull links:
link1
link2

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.