Linux: fájlok mérete egy könyvtárban

Fórumok

Egyszerűnek tűnő kérdés, de milyen parancssori program tudja összesíteni Linux-on egy adott könyvtár alatti fájlok és csak a fájlok összesített méretét?

A `du` nem jó, mert az disk blokkméretben számol, a -b vagy a --apparent-size se teljesen a fájlok méretével számol, mert rekurzív esetben látszólag hozzászámolja a könyvtárak méretét is (fájlok neve, metaadatok, egyebek):

$ ls -lR | awk {'a=a+$5; print $5" "a'}
 0
 0
106 106
3581 3687
4371 8058
2155 10213
2018 12231
106 12337

$ du
20      .

$ du -b
16433   .

$ du --apparent-size
17      .

Hozzászólások

$ du -b
16433   .

Vonj le belőle 4096-ot és az eredmény pontos.

Kifejtenéd bővebben?

$ find . -type f | xargs -d '\n' ls -l | awk '{sum=sum+$5;} END{print sum}'
14218598380
$ du -b -s
14220290028     .
$ du -bSd0
4096    .

Ez ugye annyit csinál, hogy kihagyja az összes alkönyvtárat és ha csak alkönyvtárak vannak, akkor remekül visszatér egy fix 4096 értékkel.

Ha csak fájlok vannak, akkor meg ugyanaz, mint amit írtam feljebb:

$ find . -type f | xargs -d '\n' ls -l | awk '{sum=sum+$5;} END{print sum}'
12337
$ du -b -s
16433   .
$ du -bSd0
16433   .

Már miért ne lenne jó? Azt jelenti hogy egy darab _fájl_ sincs az adott könyvtárban.

Az hogy az alkönyvtárakban vannak fájlok, az más kérdés. Kicsit félreérthetően fogalmaztál amikor "egy adott könyvtár alatti fájlok és csak a fájlok"-at írtál. Akkor ezek szerint rekurzívan értetted. Azt passzolom.

Az intellektuális kihíváson kívül ennek amúgy mi értelme van?

Annyira nem rossz felülbecslés amit a du ad, pontosan kiszámolt helyre meg úgysem tudod elrakni mert a másik FS-en is fog kelleni plusz hely a foldereknek. Oké, S3-nál mondjuk nem kell számolni. 

Esetleg mondhatnál picit többet a konkrét megoldandó problémáról hátha van másik megközelítés.

zászló, zászló, szív

Nem túl komplex: sok kis fájl sok alkönyvtárban és az ott lévő sok adat aktuális mennyisége érdekel, nem az, hogy mennyi helyet foglal el valahol, mert ráadásul dedup+compression ZFS a fájlrendszer, szóval a du vagy hülyeséget ír vagy számomra használhatatlan.

Hirtelen valami ilyesmi jutott eszembe:

find . -type f -exec stat -c %s {} \; | awk '{sum=sum+$1} END{print sum}'

De nem egy interpretált shell hajtja végre egy shell pipeline-on keresztül, egyesével exec hívással, majd egy interpretált awk program összesíti:

real    0m33.749s
user    0m23.990s
sys     0m12.624s

vs

real    0m3.761s
user    0m1.447s
sys     0m2.305s

Az mc kapcsán jut eszembe, hogy én az ilyet mindig fájlkezelőkben néztem meg, jelenleg Vifm-ben. Ennek ellenére Elbandi megoldása király, szkriptből is használható, elteszem, előre láthatóan jól fog jönni. A du-ba tényleg jól jönne ehhez egy kapcsoló, ami csak a fájlokat számolja, hiszen ha olyan kapcsoló máris van, ami csak a mappákat nézi, akkor nem értem, hogy fordítva miért nem jött össze.

Windows 95/98: 32 bit extension and a graphical shell for a 16 bit patch to an 8 bit operating system originally coded for a 4 bit microprocessor, written by a 2 bit company that can't stand 1 bit of competition.”

hatbazki, tweakeld meg az awkot hogy rendesen irja ki :)

de akar csinalhatod igy is (bar a bc limitjeit nem ismerem annyira... talan mukodik akkor is ha 1M fajl es csilli PB-rol van szo):

(find -type f -printf "%s+"; echo 0)|bc

A vegtelen ciklus is vegeter egyszer, csak kelloen eros hardver kell hozza!

Tweakeld meg te, te találtad ki ezt a sacc/kb. vackot!

 

"(find -type f -printf "%s+"; echo 0)|bc"

Na, ez már faszább, pár dolog kiderült belőle:

Nem számolja ugye bele a linkeket, de ez nem gond, ott a '-type l'.

Ha (a könyvtárak 98%-ánál) hozzáadjuk a -type l és a -type d összegeit, tökéletesen megfelel a 'du -bs'-nek, szóval hibátlam a módszer. (FIFO, socket nélkül.)

Az MC rosszul számol pl. a törött linkeknél. (Vagy pl. 324 bájt eltérést találtam a ~150 GB-os könyvtárban, de azt már tutira nem keresem meg.)

Az MC alap/egyszerű/hibátlan esetben nagyrészt hibátlanul számol.

A bc menő! :D

Erre gyanakszok én is. Nálam 64 bites rendszeren ilyen 100 gigás mappaméretnél is simán kiírta rendes számként, nem ilyen lebegőpontos közelítésként. Mindenféle külön konfigolás nélkül.

Windows 95/98: 32 bit extension and a graphical shell for a 16 bit patch to an 8 bit operating system originally coded for a 4 bit microprocessor, written by a 2 bit company that can't stand 1 bit of competition.”

# find . -type f -printf "%s\n" | original-awk '{sum=sum+$1} END{print sum}'
162395448620

# find . -type l -printf "%s\n" | original-awk '{sum=sum+$1} END{print sum}'
14

# find . -type d -printf "%s\n" | original-awk '{sum=sum+$1} END{print sum}'
15273984

 

# du -sb
162410722618    .

# echo 162395448620+14+15273984 |bc
162410722618

 

# find . -type f -printf "%s\n" | original-awk '{sum=sum+$1} END{print sum}'
162395448620

# find . -type f -printf "%s\n" | gawk '{sum=sum+$1} END{print sum}'
162395448620

# find . -type f -printf "%s\n" | mawk '{sum=sum+$1} END{print sum}'
1,62395e+11

/bin/gawk                                                                     
/bin/gawk: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=85ba7115de4e224d8c6082720f119100ddaf45f4, for GNU/Linux 3.2.0, stripped

/bin/mawk                                                                     
/bin/mawk: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=c06fdf6c2d64e90576b6fa546ecf9570adb159d8, for GNU/Linux 3.2.0, stripped

/bin/original-awk 
/bin/original-awk: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d0488e6afa71764cad5b67f1a23ae6daaec3d4ca, for GNU/Linux 3.2.0, stripped

# find . -type f -printf "%s\n" | mawk '{sum=sum+$1} END{printf "%.0f", sum}'
162395448620

# find . -type f -printf "%s\n" | gawk '{sum=sum+$1} END{printf "%.0f", sum}'
162395448620

# find . -type f -printf "%s\n" | original-awk '{sum=sum+$1} END{printf "%.0f", sum}'
162395448620

A gdu jó lehet.

https://github.com/dundee/gdu

Ha json módban futtatod, akkor kiírja az összes file-ról és könyvtárról az adatokat json formátumba. És onnan már külön tudod választani csak a file-okat, ehhez persze kell némi json parse-olás.

Szerkesztve: 2022. 05. 31., k – 16:26

Off: mi a cél? vagyis milyen hasznot jelent, ha megvan ez a szám?

A hardlink minek szamit?

---

tmp/1$ echo cucc > myfile
/tmp/1$ ln myfile mylink
/tmp/1$ ls -la
drwxr-xr-x  2 kubi kubi  4096 May 31 21:16 .
drwxrwxrwt 19 root root 12288 May 31 21:15 ..
-rw-r--r--  2 kubi kubi     5 May 31 21:15 myfile
-rw-r--r--  2 kubi kubi     5 May 31 21:15 mylink

/tmp/1$ find . -type f
./mylink
./myfile

 

Gyakorlati haszna akkor sincs sok. Felraktál volna egy ncdu-t vagy mc-t, hasonlót, azzal le tudtad volna mérni 3 mp. alatt. Jó az ncdu lehet beleszámolta volna a 4K-s mappákat, de ilyen több gigás nagyságrendnél az már összarányában nem egy nagy eltérés, képet úgy is kapsz az adatok mennyiségéről.

Persze, érdekességnek azért jó volt, lehetett belőle tanulni.

Windows 95/98: 32 bit extension and a graphical shell for a 16 bit patch to an 8 bit operating system originally coded for a 4 bit microprocessor, written by a 2 bit company that can't stand 1 bit of competition.”

Ez kétségtelen így van. Én viszont ilyenkor nem állok neki 4 kilobájtokon rugózni, mikor ilyen 100+ gigás tételeket mérek, du ilyenkor elég scriptből. Persze, elvi síkon igazad van, kéne lennie a du-ban ilyen kapcsolónak.

Windows 95/98: 32 bit extension and a graphical shell for a 16 bit patch to an 8 bit operating system originally coded for a 4 bit microprocessor, written by a 2 bit company that can't stand 1 bit of competition.”

Ragaszkodsz hozzá, hogy valamelyik built-in parancssori program csinálja? Ha van lehetőség fordítani, akkor C-ből is megoldható a mérés:

#ifndef _DIRSIZE
#define _DIRSIZE 1

#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int dirsize(char *path, size_t *size)
{
	char opath[PATH_MAX];
	DIR *d;
	struct dirent *dir;
	struct stat s;
	size_t subsize;
	int e;

	if (size == NULL)
	{
		return 1;
	}
	if (getcwd(opath, sizeof(opath)) == NULL)
	{
		return 2;
	}
	if (chdir(path) == -1)
	{
		return 3;
	}
	d = opendir(".");
	if (d == NULL)
	{
		chdir(opath);
		return 4;
	}

	*size = 0;
	while ((dir = readdir(d)) != NULL)
	{
		if (lstat(dir->d_name, &s) == 0)
		{
			if (!S_ISLNK(s.st_mode))
			{
				if (S_ISDIR(s.st_mode))
				{
					if ((strcmp(dir->d_name, ".") != 0) && (strcmp(dir->d_name, "..") != 0))
					{
						e = dirsize(dir->d_name, &subsize);
						if (e != 0)
						{
							closedir(d);
							chdir(opath);
							return e;
						}
						*size += subsize;
					}
				}
				else
				{
					*size += s.st_size;
				}
			}
		}
	}
	closedir(d);
	chdir(opath);
	return 0;
}

#endif

Nyertél, ezt a megoldást nem lehet überelni, se futási sebességben, se memóriahasználatban, se portolhatóságra. Az a baj, hogy nem vagyok ilyen badass, hogy mindent C-ben írjak magamnak, egyszeri userként ilyenkor shell sckripttel szerencsétlenkedek, ami meg futási teljesítményben sokszor nem ideális.

Windows 95/98: 32 bit extension and a graphical shell for a 16 bit patch to an 8 bit operating system originally coded for a 4 bit microprocessor, written by a 2 bit company that can't stand 1 bit of competition.”

Igazság szerint lehet még rajta optimalizálni.

Egyrészt arra a két db strcmp()-re valójában nincs szükség, elég ez a makró:

#define DENSOP(P) (((P)[0] != '.') || (((P)[1] != 0) && (((P)[1] != '.') || ((P)[2] != 0))))

és akkor így módosul az érintett sor a két komparálással:

					if ((strcmp(dir->d_name, ".") != 0) && (strcmp(dir->d_name, "..") != 0))

helyett

					if (DENSOP(dir->d_name))

(És akkor dobható az include-ok közül a string.h is.)

Másrészt - noha ez már valószínűleg nem jelent különbséget a kapott gépi kódban, mert a fordító úgyis kiszúrja - a kétszintű if is felesleges a linkvizsgálatnál:

		if (lstat(dir->d_name, &s) == 0)
		{
			if (!S_ISLNK(s.st_mode))

helyett

		if ((lstat(dir->d_name, &s) == 0) && !S_ISLNK(s.st_mode))

De mondom, ezt a fordító valószínűleg amúgy is megfogta, ez csak magát a C kódot teszi egyszerűbbé és kisebbé.

Viszont az include-ok közé kell még egy #include <limits.h> is. Linux alatt megy nélküle, de a BSD-k, vagy Solaris alatt nem fog; most próbáltam ki.

És ha már így össze lett rakva, meg ki lett próbálva, megcsináltam, hogy egy-egy flag-gel állítható legyen, hogy a könyvtárak 4096 byte-ját (vagy amennyi) azt számolja bele, vagy se, valamint, hogy oldja-e fel a symlinkeket, vagy se:

int dirsize(char *path, size_t *size, bool add_dir_sizes, bool follow_symlinks);

És úgy, hogy a flag-ek mentén négy rutinra szétbontva, hogy annyival is gyorsabb legyen (annyival kevesebb elágazás). Letölthető innen, ha valakinek kellene: http://oscomp.hu/?details/DirSize_unit_for_C_v1.0.0_(CP)_1604

Szerkesztve: 2022. 06. 01., sze – 21:17

A sebesség miatt néztem meg Ruby-ban és gyorsan fut root-ként gyökérből indítva kb 7 sec alatt 1 szálon (6 éves noti, i5-5200U CPU @ 2.20GHz). A du parancs ugyanerre 3.3 sec alatt fut. Kb fele. Szerintem érdemes leprogramoznod a választott nyelveden és akkor olyan lesz amilyen neked kell.

Dir["**/**"].select{|f| File.file?(f) }.map{|f| File.size(f) rescue 0 }.sum

281729932533277

"A `du` nem jó, mert az disk blokkméretben számol"
Miért, nem blokkosan tárulonk adatot?

Gondolom, neki az adattartalom kell, nem pedig az, hogy a konkrét filerendszeren mennyi helyet foglal. Ha elmozgatod az adatot, máshol tárolod, akkor más lehet a foglalási egység is, tehát a nettó adatmennyiség a releváns.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE