Введение
Данный раздел основан на интерфейсах файлов и директорий Linux. Этот проект revolves вокруг природы файловой системы, используя функцию lstat и операции с директориями для реализации программы для рекурсивного подсчета типов файлов. Он обеспечивает удобный способ для глубокого понимания состава типов файлов в файловой системе Linux. Кроме того, программа для подсчета типов файлов, разработанная в этом проекте, может быть использована в практических учебных и рабочих средах.
👀 Предварительный просмотр
$./file_type.
обычные файлы = 2, 66,67 %
директории = 1, 33,33 %
блочные специальные файлы = 0, 0,00 %
символьные специальные файлы = 0, 0,00 %
FIFO = 0, 0,00 %
символические ссылки = 0, 0,00 %
сокеты = 0, 0,00 %
🎯 Задачи
В этом проекте вы научитесь:
- Как реализовать программу на C, которая рекурсивно подсчитывает типы файлов в директории, используя интерфейсы файлов и директорий Linux.
🏆 Достижения
После завершения этого проекта вы сможете:
- Использовать функцию
lstatдля получения информации о файлах в Linux. - Выполнять операции с директориями, такие как открытие директорий и чтение записей директорий.
- Создать программу, которая рекурсивно подсчитывает разные типы файлов, включая обычные файлы, директории, блочные специальные файлы, символьные специальные файлы, именованные каналы, символические ссылки и сокеты.
- Вычислять и отображать процентное соотношение каждого типа файлов в директории.
Основные знания и создание файлов проекта
Далее мы представим шаги от концепции до реализации, в основном применяя следующие知识点 языка C:
- структура
statи функцияlstat, функцииopendir,readdir, структураdirent, рекурсия, вызовы функций и т.д.
В рамках всей программы мы строим функции, такие как main, myftw, dopath, myfunc и path_alloc.
- Функция
myfuncв основном обходит и подсчитывает типы файлов, соответствующие критериям. - Функция
dopathв основном рекурсивно получает пути и определяет, являются ли они директориями или файлами. - Функция
myftwначинается с получения начального адреса и размера памяти для хранения полного пути изpath_alloc. - Функция
path_allocв основном выделяет память для пути (полного пути).
Создайте новый файл с именем file_type.c в директории ~/project и откройте его в предпочитаемом редакторе кода.
cd ~/project
touch file_type.c
Проектировать главную функцию
Основная функция main заключается в том, чтобы сначала получить аргументы командной строки и проверить их валидность. Затем она вызывает функцию myftw для вычисления количества различных типов файлов. Наконец, она вычисляет процентное соотношение типов файлов и выводит их.
#include <dirent.h>
#include <limits.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define FTW_F 1 /* Флаг для недиректорийных файлов */
#define FTW_D 2 /* Флаг для директорийных файлов */
#define FTW_DNR 3 /* Флаг для нечитаемых директорийных файлов */
#define FTW_NS 4 /* Флаг для файлов, которые не могут быть доступны для stat */
static char *fullpath;
static size_t pathlen;
/* Определение функции для обработки файлов */
typedef int Myfunc(const char *, const struct stat *, int);
static Myfunc myfunc;
static int myftw(char *, Myfunc *);
static int dopath(Myfunc *);
char *path_alloc(size_t *size);
static long nreg, ndir, nblk, nchr, nfifo, nslink, nsock, ntot;
int main(int argc, char *argv[])
{
int ret;
// Проверка валидности ввода
if (argc!= 2)
{
printf("Некорректный ввод команды! \n");
exit(1);
}
/* Вычисление количества различных типов файлов */
ret = myftw(argv[1], myfunc);
/* Вычисление общего количества файлов */
ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock;
/* Избегание деления на 0 для повышения стабильности программы */
if (ntot == 0)
ntot = 1;
/* Вывод процентного соотношения различных типов файлов */
printf("обычные файлы = %7ld, %5.2f %%\n", nreg,
nreg*100.0 / ntot);
printf("директории = %7ld, %5.2f %%\n", ndir,
ndir*100.0 / ntot);
printf("блочные специальные файлы = %7ld, %5.2f %%\n", nblk,
nblk*100.0 / ntot);
printf("символьные специальные файлы = %7ld, %5.2f %%\n", nchr,
nchr*100.0 / ntot);
printf("FIFO = %7ld, %5.2f %%\n", nfifo,
nfifo*100.0 / ntot);
printf("символические ссылки = %7ld, %5.2f %%\n", nslink,
nslink*100.0 / ntot);
printf("сокеты = %7ld, %5.2f %%\n", nsock,
nsock*100.0 / ntot);
exit(ret);
}
*fullpath: Используется для хранения полного пути к файлу.pathlen: Используется для хранения длины пути к файлу.nreg: Количество обычных файлов.ndir: Количество директорийных файлов.nblk: Количество блочных специальных файлов.nchr: Количество символьных специальных файлов.nfifo: Количество именованных каналов.nslink: Количество символических ссылок на файлы.nsock: Количество файлов сокетов.ntot: Общее количество файлов.
Проектировать функцию myftw
Функция используется для обработки pathname и сохранения его в глобальном массиве символов, а затем вызывает dopath. Функция path_alloc используется для выделения памяти, и важно отметить, что fullpath - это глобальная переменная, поэтому разные функции могут удобно использовать ее. Затем вызывается функция dopath для дальнейшей обработки имени пути (является ли он директорией или нет).
static int myftw(char *pathname, Myfunc *func)
{
/* Выделение памяти для массива строк для сохранения пути */
fullpath = path_alloc(&pathlen);
/* Если выделенное пространство недостаточно для сохранения пути, используем realloc для перевыделения */
if (pathlen <= strlen(pathname)) {
pathlen = strlen(pathname) * 2;
if ((fullpath = realloc(fullpath, pathlen)) == NULL);
printf("realloc не удался!\n");
}
/* Сохраняем параметр pathname в полном пути. Обратите внимание: fullpath - это глобальная переменная
и может быть вызвана dopath */
strcpy(fullpath, pathname);
/* Вызываем функцию dopath */
return(dopath(func));
}
/* Выделение массива пути */
char *path_alloc(size_t* size)
{
char *p = NULL;
if (!size)
return NULL;
p = malloc(256);
if (p)
*size = 256;
else
*size = 0;
return p;
}
Проектировать функцию 'dopath'
int lstat(const char *path, struct stat *buf)Когда файл является символической ссылкой, lstat возвращает информацию о самой символической ссылке, в то время как stat возвращает информацию о файле, на который ссылается ссылка. Одной из используемых здесь структур данных является структураstat. Определение этой структуры выглядит так:
struct stat {
dev_t st_dev;
ino_t st_ino;
mode_t st_mode;
nlink_t st_nlink;
uid_t st_uid;
gid_t st_gid;
dev_t st_rdev;
off_t st_size;
timestruc_t st_atim;
timestruc_t st_mtim;
timestruc_t st_ctim;
blksize_t st_blksize;
blkcnt_t st_blocks;
char st_fstype[_ST_FSTYPSZ];
};
DIR* opendir (const char * path )Функция: открывает директорию, возвращает пустой указатель при неудаче.struct dirent *readdir(DIR *dir)Функция: readdir() возвращает следующую запись директории в потоке директории dir. Возвращаемое значение: При успешном выполнении readdir() возвращает указатель на следующую запись директории. При достижении конца потока директории readdir() возвращает NULL.
/* dopath используется для определения, является ли это директорией, и затем выбирает, следует ли непосредственно войти в функцию myfunc для подсчета, или рекурсивно вызывать функцию dopath. */
static int dopath(Myfunc* func)
{
struct stat statbuf;
struct dirent *dirp;
DIR *dp;
int ret, n;
/* Вызываем функцию lstat для получения информации stat о pathname. Если это не удается, вызываем функцию func и передаем FTW_NS */
if (lstat(fullpath, &statbuf) < 0)
return(func(fullpath, &statbuf, FTW_NS));
/* Проверяем st_mode структуры файла stat. Если это не директория, вызываем функцию func и передаем FTW_F, а затем определяем тип файла с помощью myfunc */
if (S_ISDIR(statbuf.st_mode) == 0)
return(func(fullpath, &statbuf, FTW_F));
/* Последний случай - pathname представляет директорию. Нормальное возвращаемое значение func равно 0, поэтому после выполнения func оно не вернется и будет продолжать рекурсивно вызывать func */
if ((ret = func(fullpath, &statbuf, FTW_D))!= 0)
return(ret);
/* Обработка пути, увеличение длины пространства для пути */
n = strlen(fullpath);
if (n + NAME_MAX + 2 > pathlen) {
pathlen *= 2;
if ((fullpath = realloc(fullpath, pathlen)) == NULL)
printf("realloc не удался!\n");
}
fullpath[n++] = '/';
fullpath[n] = 0;
/* Обработка каждой записи в директории */
if ((dp = opendir(fullpath)) == NULL)
return(func(fullpath, &statbuf, FTW_DNR));
while ((dirp = readdir(dp))!= NULL) {
if (strcmp(dirp->d_name, ".") == 0 ||
strcmp(dirp->d_name, "..") == 0)
continue; /* Игнорируем текущую директорию (.) и родительскую директорию (..) для избежания бесконечного цикла */
strcpy(&fullpath[n], dirp->d_name); /* Добавляем имя текущей записи директории после "/" */
if ((ret = dopath(func))!= 0) /* Затем рекурсивно вызываем dopath с новым pathname */
break;
}
fullpath[n-1] = 0;
/* Закрываем директорию */
if (closedir(dp) < 0)
printf("невозможно закрыть директорию %s", fullpath);
return(ret);
}
Проектировать функцию myfunc
Основная цель функции myfunc - определить тип файла на основе stat и подсчитать их. S_IFMT - это маска, используемая для интерпретации флагов st_mode.
Используются некоторые макросы для определения типа файла. Это параметризованные макросы, похожие на вызовы функций:
S_ISBLK: Проверяет, является ли это специальным файлом блока устройства.S_ISCHR: Проверяет, является ли это специальным файлом символьного устройства.S_ISDIR: Проверяет, является ли это директорией.S_ISFIFO: Проверяет, является ли это FIFO-устройством.S_ISREG: Проверяет, является ли это обычным файлом.S_ISLNK: Проверяет, является ли это символической ссылкой.S_ISSOCK: Проверяет, является ли это сокетом.
static int myfunc(const char *pathname, const struct stat *statptr, int type)
{
switch (type) {
/* Обработка недиректорийных файлов */
case FTW_F:
switch (statptr->st_mode & S_IFMT) {
case S_IFREG: nreg++; break;
case S_IFBLK: nblk++; break;
case S_IFCHR: nchr++; break;
case S_IFIFO: nfifo++; break;
case S_IFLNK: nslink++; break;
case S_IFSOCK: nsock++; break;
case S_IFDIR:
printf("для S_IFDIR для %s", pathname);
}
break;
/* Обработка директорийных файлов */
case FTW_D:
ndir++;
break;
/* Обработка нечитаемых директорий */
case FTW_DNR:
printf("%s директория нечитаема", pathname);
break;
case FTW_NS:
printf("%s ошибка при выполнении stat", pathname);
break;
default:
printf("Тип %d не распознан, pathname равен %s", type, pathname);
}
return(0);
}
Компилировать и тестировать
В терминале введите следующую команду для компиляции и запуска:
cd ~/project
gcc -o file_type file_type.c
./file_type.
labex:project/ $ ls
file_type file_type.c
labex:project/ $ gcc -o file_type file_type.c
labex:project/ $./file_type.
обычные файлы = 2, 66.67 %
директории = 1, 33.33 %
блочные специальные файлы = 0, 0.00 %
символьные специальные файлы = 0, 0.00 %
FIFO = 0, 0.00 %
символические ссылки = 0, 0.00 %
сокеты = 0, 0.00 %
Результаты показывают, что в текущей директории обычные файлы составляют 66.67%, а директории - 33.33%.
Если вам нужно подсчитать права доступа в системной директории, вы можете использовать sudo перед выполнением команды, тем не менее, все еще есть некоторые файлы, для которых права доступа не могут быть подсчитаны.
Резюме
Через обучение по этому проекту можно улучшить понимание Linux-файловой системы. Они научатся выполнять операции с директориями и получить более глубокое понимание структуры stat, которая хранит информацию о файлах. Завершив этот проект, можно разработать практический инструмент для Linux.



