56

I'm trying to calculate the total size in bytes of all files (in a directory tree) matching a filename pattern just using the shell. This is what I have so far:

find -name *.undo -exec stat -c%s {} \; | awk '{total += $1} END {print total}'

Is there an easier way to do this? I feel like there should be a simple du or find switch that does this for me but I can't find one.

To be clear I want to total files matching a pattern anywhere under a directory tree which means

du -bs *.undo

won't work because it only matches the files in the current directory.

yothenberg
  • 1,138
  • 1
  • 9
  • 16

15 Answers15

97

Try:

find . -name "*.undo" -ls | awk '{total += $7} END {print total}'

On my system the size of the file is the seventh field in the find -ls output. If your find … -ls output is different, adjust.

In this version, using the existing directory information (file size) and the built-in ls feature of find should be efficient, avoiding process creations or file i/o.

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
tpgould
  • 1,746
  • 10
  • 10
  • 2
    That should work, but the -ls option to find, and awk, are overkill for this task. – David Z Mar 01 '09 at 07:55
  • 4
    I would add "-type f" to the find command to prevent from incorrect total if there are directories matching "*.undo" glob. – Michał Šrajer Apr 15 '11 at 08:14
  • 2
    Note that if you need several patterns to match, you will have to use escaped parenthesis for the whole expression to match otherwise the `-ls` will apply only to the last pattern. For instance, if you want to match all jpeg and png files (trusting filenames), you would use `find . \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" \) -ls | awk '{total += $7} END {print total}'` (`-iname` is for case insensitive search ; also, note the space between the expression and the escaped parenthesis). – Skippy le Grand Gourou Aug 14 '13 at 09:55
  • 1
    Is this _really_ the easiest way to do `dir *.undo /s` in Linux? – Luke Puplett Mar 13 '14 at 15:12
36

With zsh, you can use extended globbing to do:

du -c **/*.undo

Chris AtLee
  • 7,798
  • 3
  • 28
  • 27
22
find -name *.undo -print0 | du -hc --files0-from=-
gerdemb
  • 11,275
  • 17
  • 65
  • 73
  • This lists all files. Is there a way to show just the total? Regardless, I'm thinking this is a 'correct answer' candidate. – Tyler Collier Jan 10 '14 at 06:11
  • 3
    I like this because it shows the size 'human readable'. @Tyler Collier to show only total use tail: `find -type f -name '*.undo' -print0 | du -hc --files0-from=- | tail -n 1` – Florian F Jan 21 '14 at 11:56
  • This is the best answer I have ever seen for doing this on linux... its also executes about a 1,000 times faster than any other answer I have seen, since du on a file executes so fast, plus it gives output in human readable terms – Myforwik Feb 06 '15 at 00:06
  • `-files0-from` doesn't work if you have too many files, I got an error about too long file name from it – szx Feb 20 '18 at 11:06
18
du -c *pattern*

This will print the total on the last line of output.

Cantillon
  • 1,588
  • 1
  • 12
  • 23
9

I have been looking at this problem too (only a year later...) - only just found this page.

Something that I found works (for me) is the following:

find /mnt/iso -name *.avi -printf "%s\n" | paste -sd+ - | bc

This will return the total size of all the .avi files in all the sub-folders below /mnt/iso

I have to give credit to radoulov for the paste command - see this page: Shell command to sum integers, one per line?

Just to add - just in case a folder matches the search term - it's a good idea to use -type f in the find command too.

Community
  • 1
  • 1
T-Monster
  • 91
  • 1
  • 1
8
find -name '*.undo' -exec wc -c {} + | tail -n 1

should give the actual total number of bytes in the files, if you don't have too many files (where "too many" is going to be a really large number, probably in the thousands). Or if you just want to get the number alone,

find -name '*.undo' -exec wc -c {} + | tail -n 1 | cut -d' ' -f 1
David Z
  • 128,184
  • 27
  • 255
  • 279
  • You should quote '*.undo', "*.undo", \*.undo, or similar; otherwise, you'll get wrong results if there exists a.undo file in the current directory. – ephemient Mar 01 '09 at 03:48
5

Python is part of most linux distributions.

import os
import fnmatch
size= 0
for path, dirs, files in os.walk( '.' ):
    for f in files:
        if fnmatch.fnmatch(f,'*.py'):
            fileSize= os.path.getsize( os.path.join(path,f) ) 
            print f, fileSize
            size += fileSize
print size

Longish, but perfectly clear and highly extensible.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
3
find -name '*.undo' -print0 | du -hc --files0-from=- | tail -n 1

Put together from gerdemb's and strager's contributions. Using du -cb should display bytes.

Community
  • 1
  • 1
xilef
  • 2,199
  • 22
  • 16
2

I use du command like this to get the number only:

du file_list | awk '{s+=$1} END {print s}'
Hoang NT
  • 43
  • 1
  • 6
0

How about this simple one.

find ./ -name *.undo | xargs wc
Dennis Mathews
  • 6,897
  • 2
  • 26
  • 39
0

Or, you can just do this:

dir=$1

for file in $dir/* ; do

 length_file=`stat -c %s $file`
 echo "File $file has length $length_file"
 length_total=`expr $length_total + $length_file`

done

echo "Total length: $length_total ."

Where stat displays a file or file system status. The argument -c means using the specified format instead of the default one, and the format sequence $s allows the display of the total size of bytes.

expr 

Just evaluates an expression.

bliss_terra
  • 119
  • 2
  • 9
0

Perl one-liner:

find . -name "*.undo" -ls | perl -lane '$t += $F[6]; END{print $t}'

The @F autosplit array starts at index $F[0] while awk fields start with $1, hence $F[6] is used instead of awk's $7

Chris Koknat
  • 3,305
  • 2
  • 29
  • 30
0

du -c | awk '/./{line=$0} END{print $1}'

Peter Cotton
  • 1,671
  • 14
  • 17
0

I think the version with xargs could be imroved (simplified) ls -1 *.undo | xargs wc

Jacob
  • 1
-3

Check the du (disk usage) command.

Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838