10

I'd like to find a series of files (based on a wildcard expression) and total their disk usage.

Something like this:

$ find . -name 'flibble*' -ctime +90 -exec du -sh {} \;

2.1G    ./flibble_116.log
2.1G    ./flibble_83.log
2.1G    ./flibble_211040_157.log
2.1G    ./flibble3747_51.log

This work. But it doesn't produce the result I'm looking for. It lists the space used by each file, as find is iterating through them.

What I want is the total du for all of the files found.

11
+50

Solution

By supplying the option -c (or --total) to du(1), you can instruct it to produce a grand total. If your implementation of du(1) supports either of these options, you can achieve the desired effect using the following command:

$ find . -name 'flibble*' -ctime +90 -exec du -shc {} +

Explanation

The difference between the semantics of -exec command {} \; and -exec command {} + is the following:

  • command {} \; executes command once for each result of find. The pathname of the result is passed instead of {}.

    $ touch 1 2 3
    $ find  1 2 3 -maxdepth 0 -exec echo {} \;
    1
    2
    3
    
  • command {} + executes command, when all the results have been retrieved. The pathnames of the results are passed instead of {}.

    $ touch 1 2 3
    $ find  1 2 3 -maxdepth 0 -exec echo {} +
    1 2 3
    

To learn more about the options of du(1) and find(1), you should consult the respective manpages.

  • 2
    You might end up with multiple totals if the number of files is important (1K+) because of the number of command line arguments limitation. – ychaouche Jan 8 '18 at 10:39
  • I can confirm @ychaouche, I got the problem while trying to assess the size of more than 30k files. – Adrien H Mar 28 at 17:07
  • If that's an issue, some implementations of du(1) also support reading the filenames from a file: find 1 2 3 -maxdepth 0 -print0 > filenames; du -shc --files0-from=filenames. – Witiko Apr 16 at 17:03
4

Try this:

du -c `find . -name 'flibble*' -ctime +90` | tail -1

The original command is giving du one argument, then executing it, until it goes through all the arguments. This way, you simply are giving it all the arguments at once, then cutting off the separate sizes, and leaving only the total. You can remove the pipe and tail to show the size of each file if you'd like.

  • This won't produce correct results with pathnames containing spaces. The right way to do this is using the -exec du -c {} + option of find, which will pass the pathnames unaltered to du. – Witiko Dec 19 '14 at 6:56
3

You can try this:

find . -name 'flibble*' -ctime +90 -exec du -ch {} + | grep total
2

I would have find itself print out the size, and use another tool to calculate the total:

find . -name 'flibble*' -ctime +90 -printf "%s\n" |
perl -lnE '$sum += $_} END {say $sum'

If you also want to see the filenames:

find . -name 'flibble*' -ctime +90 -printf "%s\t%p\n" |
perl -apE '$sum += $F[0]} END {say $sum'
1

One liner that should work to get a Gigabyte total on most systems:

echo "$(( ($(find . -name 'flibble*' -ctime +90 -type f -printf '%k+' )0)/1024/1024 )) GB"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.