Take the 2-minute tour ×
Ask Ubuntu is a question and answer site for Ubuntu users and developers. It's 100% free, no registration required.

Is there a way to get a break down of a disk (or directory) utilization based upon file-type from the command line?

I guess I could write a bash script to find+du based upon a few known file-types, but I'd rather not recreate the wheel if there is a package that already exists to parse this.

share|improve this question

migrated from stackoverflow.com Nov 22 '11 at 3:07

This question came from our site for professional and enthusiast programmers.

1 Answer 1

up vote 1 down vote accepted

I wrote up this utility that should work pretty swiftly, here is the output on my home dir for a default run:

??? 3897697700  (717MiB)
.tgz    1550090721  (1478MiB)
.jpg    872736861   (832MiB)
.iso    804329472   (767MiB)
.pack   636183905   (606MiB)
.gch    528345920   (503MiB)
.d  384725346   (366MiB)
.i  354098997   (337MiB)
.sqlite 302110738   (288MiB)
.html   233729943   (222MiB)
.cs 209534627   (199MiB)
.dll    198655123   (189MiB)
.xml    192561101   (183MiB)
.pdf    184729508   (176MiB)
.deb    173972838   (165MiB)

Note the large ??? post is due to the many .hg and .git source repositories that have no extensions.

The code defaults to printing the top-15 largest contributing filetypes, although that could easily be extended.

There is a TODO in the code to use libmagic to get the actual filetypes.

Here is the code:

#define _XOPEN_SOURCE 500
#include <ftw.h>
#include <cstdio>
#include <cstdlib>
#include <stdint.h>

#include <set>
#include <vector>
#include <algorithm>
#include <string>
#include <iostream>

static uintmax_t total        = 0ul;
static uintmax_t files        = 0ul;
static uintmax_t directories  = 0ul;
static uintmax_t symlinks     = 0ul;
static uintmax_t inaccessible = 0ul;
static uintmax_t blocks512    = 0ul;

struct byfiletype {
    std::string filetype;
    mutable uintmax_t total; // non-key

    bool operator<(const byfiletype& other) const { return filetype<other.filetype; }
    static bool largest_first(const byfiletype& a, const byfiletype& b) { return a.total>b.total; }
};

typedef std::set<byfiletype> sizemap;
static sizemap per_filetype;

std::string get_filetype(std::string fname) // TODO use libmagic to do file type detection?
{
    size_t pos = fname.rfind("/");
    if (std::string::npos != pos) fname = fname.substr(pos+1);

    pos = fname.rfind(".");

    return (std::string::npos != pos)
        ? fname.substr(pos)
        : "???";
}

static int
display_info(const char *fpath, const struct stat *sb,
             int tflag, struct FTW *ftwbuf)
{
    switch(tflag)
    {
        case FTW_D:
        case FTW_DP:  directories++;  break;
        case FTW_NS:
        case FTW_SL:
        case FTW_SLN: symlinks++;     break;
        case FTW_DNR: inaccessible++; break;
        case FTW_F:   
                      files++; 
                      byfiletype entry = { get_filetype(fpath), sb->st_size };
                      sizemap::iterator match = per_filetype.find(entry);

                      if (match != per_filetype.end()) 
                          match->total += sb->st_size;
                      else 
                          per_filetype.insert(entry);

                      break;
    }
    total += sb->st_size;
    blocks512 += sb->st_blocks;
    return 0; /* To tell nftw() to continue */
}

int
main(int argc, char *argv[])
{
    int flags = FTW_DEPTH | FTW_MOUNT | FTW_PHYS;

    if (nftw((argc < 2) ? "." : argv[1], display_info, 20, flags) == -1)
    {
        perror("nftw");
        exit(EXIT_FAILURE);
    }

    printf("Total size: %7jd\n", total);
    printf("In %jd files and %jd directories (%jd symlinks and %jd inaccessible directories)\n", files, directories, symlinks, inaccessible);
    printf("Size on disk %jd * 512b = %jd\n", blocks512, blocks512<<9);

    size_t N = std::min(15ul, per_filetype.size());
    typedef std::vector<byfiletype> topN_t;
    topN_t topN(N);
    std::partial_sort_copy(
            per_filetype.begin(), per_filetype.end(),
            topN.begin(), topN.end(),
            byfiletype::largest_first);

    for (topN_t::const_iterator it=topN.begin(); it!=topN.end(); ++it)
    {
        std::cout << it->filetype << "\t" << it->total << "\t(" << ((it->total)>>20) << "MiB)" << std::endl;
    }

    exit(EXIT_SUCCESS);
}
share|improve this answer
    
nevermind I was using the wrong compiler. This seems to work rather nicely. Thanks –  BassKozz Nov 21 '11 at 0:57

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.