Eu escrevi este utilitário que deve funcionar muito rapidamente, aqui está a saída no meu diretório home para uma execução padrão:
??? 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)
Observe que a grande ???
post se deve aos muitos repositórios de origem .hg e .git que não possuem extensões.
O código padrão é imprimir os 15 maiores tipos de arquivos contribuintes, embora isso possa ser facilmente estendido.
Há um TODO no código para usar libmagic
para obter os tipos de arquivos reais.
Aqui está o código:
#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);
}