HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Clean way to get size of directory

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
directorysizewaygetclean

Problem

I'm working on a Unix machine where I can't use more than vanilla Perl, and I'm using Perl5.8. This script exits with a 1 if the current directory size is smaller than 1 GB (the character after -d is a literal "tab" character).

my $du = `du --si | tail -1 | cut -d"   " -f1`;
chomp $du;
if (substr($du, -1) ne "G") {
    exit 1;
}
exit 0;


This is gross, but I know the data is in du --si so I can write it in 30 seconds. Is there a cleaner, more robust way?

Solution

I agree with @rolfl that this would be much simpler as a one-line shell pipeline. The -s option to du makes it produce a total. awk is a good tool to use for processing multi-column text.

du -s --si | awk '$1 ~ /G/ { exit 1 }'


However, the --si option seems to be a non-portable GNU extension. A more portable version would look at the number of 512-byte blocks. The magic number 1953125 is \$\dfrac{10^9}{512}\$.

du -s | awk '$1 < 1953125 { exit 1 }'


The second version also works even if the total is in the terabyte or exabyte range.

There is an inefficiency, though: you should be able to exit early as soon as you find that the total exceeds 1 GB. For that, you would go back to Perl, but with a proper Perl program instead of a wrapper around du.

use File::Find;
use strict;

my $sum = 0;
my %seen_inodes;
find(sub {
    my ($inode, $blocks) = (stat)[1, 12] or die "${File::Find::name}: $!";

    # Do not double-count hard links
    if (!$seen_inodes{$inode}) {
        $seen_inodes{$inode} = 1;
        $sum += 512 * $blocks;
        exit 0 if $sum >= 1_000_000_000;
    }
}, ".");
exit 1;

Code Snippets

du -s --si | awk '$1 ~ /G/ { exit 1 }'
du -s | awk '$1 < 1953125 { exit 1 }'
use File::Find;
use strict;

my $sum = 0;
my %seen_inodes;
find(sub {
    my ($inode, $blocks) = (stat)[1, 12] or die "${File::Find::name}: $!";

    # Do not double-count hard links
    if (!$seen_inodes{$inode}) {
        $seen_inodes{$inode} = 1;
        $sum += 512 * $blocks;
        exit 0 if $sum >= 1_000_000_000;
    }
}, ".");
exit 1;

Context

StackExchange Code Review Q#48626, answer score: 4

Revisions (0)

No revisions yet.