Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

How do I get the actual directory size, using UNIX/Linux standard tools?

Alternative question: How do I get du to show me the actual directory size?

Since people seem to have different definitions of the term "size": My definition of "directory size" is the sum of all regular files within that directory.

I do NOT care about the size of the directory inode or whatever (blocks * block size) the files take up on the respective file system. A directory with 3 files, 1 byte each, has a directory size of 3 bytes (by my definition).

Calculating the directory size seems to be very unreliable.
For example, an empty directory is usually 4096 bytes in size, according to du -b. This would be the block size (tune2fs -l reports a block size of 4096 for the file system I used in this case) - I expect zero, especially because the -b option includes the --apparent-size option, which is supposed to "print [the] apparent sizes, rather than disk usage".
A directory with a 1 byte file (echo -n "a" >foo) is 4097 bytes in size, so it looks like du adds the size of the directory inode itself to the total.

More importantly, I've seen extreme differences with large directories.
There is a directory which a total size of 314086500373 B (about 293 GiB), I got this number using a cheap shell script, which basically finds (find -type f) all the files and gets their size based on the output of ls.
Since parsing the output of ls is error-prone, I also wrote a simple C++ program which calculates the directory size without the help of any other tools and it confirmed the directory size of 314086500373 B (it really just contains a lot of regular files in a few subdirectories, no links or other special files).

Here is the problem:
I moved this directory to another file system on my Linux box and I simply want to compare the directory size before and after (and during the process), which turned out to be difficult.
In the old location, du -hs reported a size of "181G". That's just wrong. du -b (no -s) gives "314086500847" (B!?) for this directory, which is already (+474 B) more than the actual total directory size in bytes.
In the new location, du -hs now reports a size of "586G". The directory is still the same, I have verified the MD5 sums of all files in this directory (before and after) and they match. (The files are supposed to use more disk space in the new location, but that's not what I want to know from du.) du -bs shows "314086500847" (at least like before). Both the shell script and my program calculate the old/correct directory size of 314086500373 B (same as in the old location).
An error of -112 G / +293 G is unacceptable. Simply calculating the total (logical) size of a directory should be the easiest thing in the world.

So what (tool/option) has to be used to get the actual directory size (maybe I'm just using du wrong)?
Preferably using standard tools, but if there is a "better version of du" that'd work too.


Update:
Several answers have already been posted. This works on Linux:

find $DIR -type f -print0 | du -scb --files0-from=- | tail -n 1

This also works on FreeBSD ("du: illegal option -- b"):

ls -lnR $DIR | grep -v '^d' | awk '{total += $5} END {print total, "Total"}'

It's a bit to type but it calculates the correct directory size.

My second question (a human-readable result which is NOT off by over 100 GB) has been answered with a nice awk script.

share|improve this question
    
What filesystem is used in the new location — is it xfs by any chance? –  Sergey Vlasov Jun 2 '13 at 16:32
1  
    
And if your new FS is really XFS, the greatly increased disk usage is probably due to aggressive preallocation, which decreases file fragmentation at the cost of disk usage. –  Sergey Vlasov Jun 2 '13 at 16:51
    
@Sergey Vlasov: Not XFS, it's ZFS. And I do know why the files use less disk space (compression=on) in the old location and more (copies=2) in the new location, but that's not the question. Thanks for the link, but the first ls|awk pipe calculates a wrong size. The second ls|awk pipe works (in my case, without special files), because it ignores directories. –  basic6 Jun 3 '13 at 16:52
add comment

5 Answers

up vote 2 down vote accepted

Here is a script displaying a human readable directory size using Unix standard tools (POSIX).

#!/bin/sh
find ${1:-.} -type f -exec ls -lnq {} \+ | awk '
function pp() {
  u="+Ki+Mi+Gi+Ti";
  split(u,unit,"+");
  v=sum;
  r=0;
  for(i=1;i<5;i++) {
    if(v<1024) break;
    r=v%1024;
    v/=1024;
  }
  printf("%.3f %sB\n",v+r/1024.,unit[i]);
}
{sum+=$5}
END{pp()}'

eg:

$ ds ~        
72.891 GiB
share|improve this answer
    
And now I found another option which is missing in all suggested ls invocations here: -q. Without this option the script will break if some file name contains newline characters. Writing really reliable shell scripts is too hard… –  Sergey Vlasov Jun 3 '13 at 20:17
    
@SergeyVlasov The script I posted shouldn't break with such files, only merely ignoring the extra lines. The only problem case would occur should a carefully crafted file had an extra line witha fifth colon that contains a numerical value. Your suggestion would indeed avoid that situation. Thanks for the tip, script updated. –  jlliagre Jun 3 '13 at 20:47
add comment

Assuming you have du from GNU coreutils, this command should calculate the total apparent size of arbitrary number of regular files inside a directory without any arbitrary limits on the number of files:

find . -type f -print0 | du -scb --files0-from=- | tail -n 1

Add the -l option to du if there are some hardlinked files inside, and you want to count each hardlink separately (by default du counts multiple hardlinks only once).

The most important difference with plain du -sb is that recursive du also counts sizes of directories, which are reported differently by different filesystems; to avoid this, the find command is used to pass only regular files to du. Another difference is that symlinks are ignored (if they should be counted, the find command should be adjusted).

This command will also consume more memory than plain du -sb, because using the --files0-from=FILE makes du store device and inode numbers of all processed files, as opposed to the default behavior of remembering only files with more than one hard link. (This is not an issue if the -l option is used to count hardlinks multiple times, because the only reason to store device and inode numbers is to skip hardlinked files which had been already processed.)

If you want to get a human-readable representation of the total size, just add the -h option (this works because du is invoked only once and calculates the total size itself, unlike some other suggested answers):

find . -type f -print0 | du -scbh --files0-from=- | tail -n 1

or (if you are worried that some effects of -b are then overridden by -h)

find . -type f -print0 | du -sc --apparent-size -h --files0-from=- | tail -n 1
share|improve this answer
    
Not sure what to do for FreeBSD — although -b could probably be replaced by -A -B 1, there is no equivalent for --files0-from=-, and using xargs will need some workarounds in case the file list is bigger than ARG_MAX (and some external solution for human-readable output). –  Sergey Vlasov Jun 3 '13 at 19:59
add comment

If all you want is the size of the files, excluding the space the directories take up, you could do something like

find . -type f -print0 | xargs -0 du -scb | tail -n 1

@SergeyVlasov pointed out that this will fail if you have more files than argmax. To avoid that you could use something like:

find . -type f -exec du -sb '{}' \; | gawk '{k+=$1}END{print k}'
share|improve this answer
    
This command will silently give a wrong result if the directory contains so many files that they don't fit in the limit on execve() arguments size — in this case xargs will invoke du multiple times, and each invocation will print grand total just for its part of the complete file list, then tail will show just the total size of the last part. –  Sergey Vlasov Jun 2 '13 at 17:05
    
@SergeyVlasov good point, I hadn't thought of that, thanks, answer updated. –  terdon Jun 2 '13 at 17:19
    
Thanks for the suggestion, those find|du pipes calculate the correct directory size in bytes. However, I was kind of hoping for an easier solution. Also how do I get a human readable result which is NOT off by over 100 GB? –  basic6 Jun 3 '13 at 16:56
add comment

Just an alternative, using ls:

ls -nR | grep -v '^d' | awk '{total += $5} END {print total, "Total"}'

ls -nR: -n like -l, but list numeric UIDs and GIDs and -R list subdirectories recursively.

grep -v: Invert the sense of matching, to select non-matching lines. (-v is specified by POSIX .). '^ d' will exclude the directories.

Ls command: http://linux.about.com/od/commands/l/blcmdl1_ls.htm

Man Grep: http://linux.die.net/man/1/grep

EDIT:

Edited as the suggestion @ Sergey Vlasov.

share|improve this answer
    
Using the -n option for ls instead of -l (show UID/GID numbers instead of names) is safer, because user and group names can contain spaces (e.g., if winbind or sssd is used to join the system to a Windows domain, you can get group names like domain users). It should also be faster due to not needing to lookup user and group names. –  Sergey Vlasov Jun 3 '13 at 4:49
add comment

Some versions of du support the argument --apparent-size to show apparent size instead of disk usage. So your command would be:

du -hs --apparent-size

From the man pages for du included with Ubuntu 12.04 LTS:

--apparent-size
      print apparent sizes,  rather  than  disk  usage;  although  the
      apparent  size is usually smaller, it may be larger due to holes
      in (`sparse') files, internal  fragmentation,  indirect  blocks,
      and the like
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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