49

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
jww
  • 97,681
  • 90
  • 411
  • 885
xfloys2112
  • 605
  • 1
  • 5
  • 13
  • You can add: -print | uniq | xargs ls -1S...... – Josef Klimuk Jun 03 '18 at 10:05
  • Have a look at this answer for a reasonably clean way to solve the problem: https://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. – Samuel Åslund May 03 '23 at 09:34

4 Answers4

49

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

Community
  • 1
  • 1
planetmaker
  • 5,884
  • 3
  • 28
  • 37
17

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

David Espart
  • 11,520
  • 7
  • 36
  • 50
3

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.

Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
gws
  • 459
  • 1
  • 7
  • 16
  • 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. – gws May 03 '18 at 22:20
0

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

mattalxndr
  • 9,143
  • 8
  • 56
  • 87