Introduction
Ce chapitre est basé sur les interfaces de fichiers et de répertoires de Linux. Ce projet tourne autour de la nature du système de fichiers, utilisant la fonction lstat et les opérations de répertoire pour implémenter un programme de comptage récursif des types de fichiers. Il fournit un moyen pratique de comprendre en profondeur la composition des types de fichiers dans le système de fichiers Linux. De plus, le programme de comptage de types de fichiers développé dans ce projet peut être utilisé dans des environnements d'apprentissage et de travail pratiques.
👀 Aperçu
$./file_type.
fichiers normaux = 2, 66,67 %
répertoires = 1, 33,33 %
spéciaux de bloc = 0, 0,00 %
spéciaux de caractère = 0, 0,00 %
FIFO = 0, 0,00 %
liens symboliques = 0, 0,00 %
sockets = 0, 0,00 %
🎯 Tâches
Dans ce projet, vous allez apprendre :
- Comment implémenter un programme en C qui compte récursivement les types de fichiers dans un répertoire en utilisant les interfaces de fichiers et de répertoires de Linux.
🏆 Réalisations
Après avoir terminé ce projet, vous serez capable de :
- Utiliser la fonction
lstatpour obtenir des informations sur les fichiers sous Linux. - Effectuer des opérations de répertoire telles que l'ouverture de répertoires et la lecture des entrées de répertoire.
- Créer un programme qui compte récursivement différents types de fichiers, y compris les fichiers normaux, les répertoires, les fichiers spéciaux de bloc, les fichiers spéciaux de caractère, les tubes nommés, les liens symboliques et les sockets.
- Calculer et afficher le pourcentage de chaque type de fichier dans un répertoire.
Connaissances de base et création de fichiers de projet
Ensuite, nous allons présenter les étapes allant de la conception à la mise en œuvre, en appliquant principalement les points de connaissances du langage C suivants :
- Structure
statet fonctionlstat, fonctionsopendir,readdir, structuredirent, récursion, appels de fonctions, etc.
Dans l'ensemble du programme, nous construisons des fonctions telles que main, myftw, dopath, myfunc et path_alloc.
- La fonction
myfunctraverse principalement et compte les types de fichiers qui répondent aux critères. - La fonction
dopathobtient principalement les chemins de manière récursive et détermine s'ils sont des répertoires ou des fichiers. - La fonction
myftwcommence par obtenir l'adresse de départ et la taille de l'espace mémoire pour stocker le chemin complet à partir depath_alloc. - La fonction
path_allocalloue principalement de l'espace mémoire pour le chemin (chemin complet).
Créez un nouveau fichier nommé file_type.c dans le répertoire ~/project, puis ouvrez-le dans votre éditeur de code préféré.
cd ~/project
touch file_type.c
Concevoir la fonction principale
La fonction principale de la fonction main est d'abord de recevoir les arguments de ligne de commande et de vérifier leur validité. Ensuite, elle appelle la fonction myftw pour calculer le nombre de différents types de fichiers. Enfin, elle calcule le pourcentage des types de fichiers et les affiche.
#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 /* Drapeau pour les fichiers non-répertoires */
#define FTW_D 2 /* Drapeau pour les fichiers de répertoire */
#define FTW_DNR 3 /* Drapeau pour les fichiers de répertoire non-lisibles */
#define FTW_NS 4 /* Drapeau pour les fichiers qui ne peuvent pas être accessibles par stat */
static char *fullpath;
static size_t pathlen;
/* Définition de la fonction pour traiter les fichiers */
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;
// Vérification de la validité de l'entrée
if (argc!= 2)
{
printf("Entrée de commande invalide! \n");
exit(1);
}
/* Calculer le nombre de différents types de fichiers */
ret = myftw(argv[1], myfunc);
/* Calculer le nombre total de fichiers */
ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock;
/* Éviter la division par 0 pour améliorer la stabilité du programme */
if (ntot == 0)
ntot = 1;
/* Afficher le pourcentage de différents types de fichiers */
printf("fichiers normaux = %7ld, %5.2f %%\n", nreg,
nreg*100.0 / ntot);
printf("répertoires = %7ld, %5.2f %%\n", ndir,
ndir*100.0 / ntot);
printf("spéciaux de bloc = %7ld, %5.2f %%\n", nblk,
nblk*100.0 / ntot);
printf("spéciaux de caractère = %7ld, %5.2f %%\n", nchr,
nchr*100.0 / ntot);
printf("FIFO = %7ld, %5.2f %%\n", nfifo,
nfifo*100.0 / ntot);
printf("liens symboliques = %7ld, %5.2f %%\n", nslink,
nslink*100.0 / ntot);
printf("sockets = %7ld, %5.2f %%\n", nsock,
nsock*100.0 / ntot);
exit(ret);
}
*fullpath: Utilisé pour stocker le chemin complet du fichier.pathlen: Utilisé pour stocker la longueur du chemin du fichier.nreg: Le nombre de fichiers normaux.ndir: Le nombre de fichiers de répertoire.nblk: Le nombre de fichiers spéciaux de bloc.nchr: Le nombre de fichiers spéciaux de caractère.nfifo: Le nombre de tubes nommés.nslink: Le nombre de fichiers de liens symboliques.nsock: Le nombre de fichiers de socket.ntot: Nombre total de fichiers.
Concevoir la fonction myftw
Cette fonction est utilisée pour traiter pathname et le sauvegarder dans un tableau de caractères global, puis appeler dopath. La fonction path_alloc est utilisée pour allouer de l'espace, et il est important de noter que fullpath est une variable globale, de sorte que différentes fonctions peuvent l'utiliser commodément. Ensuite, appelez la fonction dopath pour traiter en profondeur le nom de chemin (qu'il s'agisse d'un répertoire ou non).
static int myftw(char *pathname, Myfunc *func)
{
/* Allouer de l'espace pour le tableau de chaînes pour sauvegarder le chemin */
fullpath = path_alloc(&pathlen);
/* Si l'espace alloué n'est pas suffisant pour sauvegarder le chemin, utiliser realloc pour réallouer */
if (pathlen <= strlen(pathname)) {
pathlen = strlen(pathname) * 2;
if ((fullpath = realloc(fullpath, pathlen)) == NULL);
printf("realloc a échoué!\n");
}
/* Sauvegarder le paramètre pathname dans le chemin complet. Attention : fullpath est une variable globale
et peut être appelée par dopath */
strcpy(fullpath, pathname);
/* Appeler la fonction dopath */
return(dopath(func));
}
/* Allocation du tableau de chemins */
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;
}
Concevoir la fonction 'dopath'
int lstat(const char *path, struct stat *buf)Lorsque le fichier est un lien symbolique, lstat renvoie des informations sur le lien symbolique lui-même, tandis que stat renvoie des informations sur le fichier vers lequel le lien pointe. Une structure de données utilisée ici est la structurestat. La définition de cette structure est la suivante :
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 )Fonction : ouvre un répertoire, renvoyant un pointeur nul en cas d'échec.struct dirent *readdir(DIR *dir)Fonction : readdir() renvoie l'entrée de répertoire suivante dans le flux de répertoire dir. Valeur de retour : En cas de réussite, readdir() renvoie un pointeur vers l'entrée de répertoire suivante. Lorsqu'on atteint la fin du flux de répertoire, readdir() renvoie NULL.
/* dopath est utilisée pour déterminer si c'est un répertoire, puis choisir de directement entrer dans la fonction myfunc pour compter, ou d'appeler récursivement la fonction dopath. */
static int dopath(Myfunc* func)
{
struct stat statbuf;
struct dirent *dirp;
DIR *dp;
int ret, n;
/* Appeler la fonction lstat pour obtenir les informations stat du pathname. Si cela échoue, appeler la fonction func et passer FTW_NS */
if (lstat(fullpath, &statbuf) < 0)
return(func(fullpath, &statbuf, FTW_NS));
/* Vérifier le st_mode de la structure stat du fichier. Si ce n'est pas un répertoire, appeler la fonction func et passer FTW_F, puis déterminer le type de fichier par myfunc */
if (S_ISDIR(statbuf.st_mode) == 0)
return(func(fullpath, &statbuf, FTW_F));
/* Le dernier cas est que le pathname représente un répertoire. La valeur de retour normale de func est 0, donc après avoir exécuté func, elle ne renverra pas et continuera à appeler récursivement func */
if ((ret = func(fullpath, &statbuf, FTW_D))!= 0)
return(ret);
/* Traitement du chemin, agrandir la longueur de l'espace du chemin */
n = strlen(fullpath);
if (n + NAME_MAX + 2 > pathlen) {
pathlen *= 2;
if ((fullpath = realloc(fullpath, pathlen)) == NULL)
printf("realloc a échoué!\n");
}
fullpath[n++] = '/';
fullpath[n] = 0;
/* Traiter chaque entrée dans le répertoire */
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; /* Ignorer le répertoire courant (.) et le répertoire parent (..) pour éviter une boucle infinie */
strcpy(&fullpath[n], dirp->d_name); /* Ajouter le nom de l'entrée de répertoire actuelle après "/" */
if ((ret = dopath(func))!= 0) /* Ensuite, appeler récursivement dopath avec le nouveau pathname */
break;
}
fullpath[n-1] = 0;
/* Fermer le répertoire */
if (closedir(dp) < 0)
printf("impossible de fermer le répertoire %s", fullpath);
return(ret);
}
Concevoir la fonction myfunc
Le principal but de la fonction myfunc est de déterminer le type de fichier en fonction de stat et de les compter. S_IFMT est un masque utilisé pour interpréter les drapeaux st_mode.
Il existe quelques définitions de macros utilisées pour aider à déterminer le type de fichier. Il s'agit de macros paramétrées, similaires à des appels de fonction :
S_ISBLK: Vérifier si c'est un fichier de périphérique de bloc spécial.S_ISCHR: Vérifier si c'est un fichier de périphérique de caractère spécial.S_ISDIR: Vérifier si c'est un répertoire.S_ISFIFO: Vérifier si c'est un périphérique FIFO.S_ISREG: Vérifier si c'est un fichier normal.S_ISLNK: Vérifier si c'est un lien symbolique.S_ISSOCK: Vérifier si c'est une socket.
static int myfunc(const char *pathname, const struct stat *statptr, int type)
{
switch (type) {
/* Gestion des fichiers non-répertoires */
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("for S_IFDIR for %s", pathname);
}
break;
/* Gestion des fichiers de répertoire */
case FTW_D:
ndir++;
break;
/* Gestion des répertoires non-lisibles */
case FTW_DNR:
printf("%s répertoire est non-lisible", pathname);
break;
case FTW_NS:
printf("%s erreur dans stat", pathname);
break;
default:
printf("Type %d est inconnu, pathname est %s", type, pathname);
}
return(0);
}
Compiler et tester
Entrez la commande suivante dans le terminal pour compiler et exécuter :
cd ~/projet
gcc -o file_type file_type.c
./file_type.
labex:projet/ $ ls
file_type file_type.c
labex:projet/ $ gcc -o file_type file_type.c
labex:projet/ $./file_type.
fichiers normaux = 2, 66,67 %
répertoires = 1, 33,33 %
spéciaux de bloc = 0, 0,00 %
spéciaux de caractère = 0, 0,00 %
FIFO = 0, 0,00 %
liens symboliques = 0, 0,00 %
sockets = 0, 0,00 %
Les résultats montrent que dans le répertoire actuel, les fichiers normaux représentent 66,67 % et les répertoires 33,33 %.
Si vous avez besoin de compter les autorisations dans le répertoire du système, vous pouvez utiliser sudo avant d'exécuter la commande. Même ainsi, il y a toujours certains fichiers pour lesquels les autorisations ne peuvent pas être comptées.
Résumé
Grâce à la formation de ce projet, on peut améliorer sa compréhension du système de fichiers Linux. On apprendra à effectuer des opérations sur les répertoires et à mieux comprendre la structure stat qui stocke les informations sur les fichiers. En complétant ce projet, on peut développer un outil Linux pratique.



