Ostatnio zadaniem dodatkowym na laboratorium z systemów operacyjnych było napisanie funkcji, która przejrzy, podany argumentem katalog, rekurencyjnie oraz zliczy rozmiar znajdujących się w nim wszystkich plików. Niby nic wielkiego, ale poziom trudności dla mnie podwyższyła przede wszystkim możliwość pisania tylko w czystym języku C z wykorzystaniem API unix/linux – na codzień piszę jednak w językach wyższego poziomu,
Ale do rzeczy. Napisałem pierwszą wersję funkcji i dumny z siebie, że tak szybko poszło odpaliłem. Zdziwienie było niesamowite jak zobaczyłem, że funkcja rozpoznaje katalogi jako pliki. Co więcej, rozmiary plików były różne (czasami zdecydowanie różne) od rzeczywistych rozmiarów jakie uzyskałem stosując komendę
ls -alR
Pierwsze co zrobiłem to zajrzałem do książki Rochkinda “Programowanie w systemie unix dla zaawansowanych” i sprawdziłem jak się sprawdza czy dany wpis jest katalogiem. Ujrzałem dwie wersje kodu, w tym jedna identyczna jak z mojej funkcji. Spróbowałem tej drugiej – niestety bez pozytywnego rezultatu. Sprawdziłem też funkcję stat() z API linuksa czy czasem nie ma jakiegoś myku przy jej stosowaniu. Pech chciał, że albo przeoczyłem fakt, o którym zaraz napiszę, albo po prostu nie było tam nic o tym napisane.
Rozwiązanie nie jest dla mnie oczywiste. Prawdę mówiąc nie wiem dlaczego tak jest, ale spróbuję się dowiedzieć. Problemem było podawanie względnej ścieżki do katalogu a potem do kolejnych wpisów w katalogu. Jak tylko podałem pełną ścieżkę, a następnie w funkcji odtwarzałem ją dla kolejnych elementów to wszystko zaczęło ładnie działać. Dlatego też poniżej zamieszczam pierwszą wersję działającego kodu:
int dir_size( char* dirname )
{
off_t size = 0;
char path[200];
struct stat tmp;//1
DIR* cat = opendir( dirname );
struct dirent* entry;
while( ( entry = readdir( cat ) ) )
{
if( !( strcmp( entry->d_name, “.” ) == 0 || strcmp( entry->d_name, “..” ) == 0 ) )//2
{
path[0] = 0;//3
strcat(path, dirname );
strcat(path,”/”);
strcat(path, entry->d_name);
stat( path, &tmp );//4if( S_ISDIR( tmp.st_mode ) )//5
{
printf( “Katalog %s \n”, entry->d_name );
size += dir_size( path );
}
else
{
size += tmp.st_size;
}
//printf( “Plik: %s rozmiar:%8d x\n”, entry->d_name, (int)tmp.st_size );
}
}return (int)size;
}
Przyszedł czas na krótkie omówienie kodu. W ramach komentarzy zaznaczyłem numerki, którymi się teraz posłużę:
- Zmienna, która będzie przechowywała dane na temat statusu pliku w systemie
- Warunek, który sprawdza czy nie mamy do czynienia z plikiem o nazwie “.” lub “..”, żeby się rekurencyjnie nie “zakręcić”
- Budowanie pełnej ścieżki dla kolejnego wpisu, aby móc poprawnie odczytać jego status
- Odczytanie statusu pliku
- Sprawdzenie czy wpis jest katalogiem. Jeśli jest to rekurencyjnie wywołujemy funkcję, a jeśli nie jest to sumujemy rozmiar
Można tu dużo jeszcze poprawić. Umieściłem pierwszą wersję tej funkcji. Można przykładowo zmienić ją tak, żeby po podaniu ścieżki względnej, zbudować pełną samemu – wykorzystać funkcję getcwd(), która zwraca Current Working Directory.
0 Odpowiedzi do “Rekurencyjne przeglądanie katalogów w unix w języku C”