Statistiques de type de fichier en utilisant C

CCBeginner
Pratiquer maintenant

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

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 lstat pour 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 stat et fonction lstat, fonctions opendir, readdir, structure dirent, 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 myfunc traverse principalement et compte les types de fichiers qui répondent aux critères.
  • La fonction dopath obtient principalement les chemins de manière récursive et détermine s'ils sont des répertoires ou des fichiers.
  • La fonction myftw commence par obtenir l'adresse de départ et la taille de l'espace mémoire pour stocker le chemin complet à partir de path_alloc.
  • La fonction path_alloc alloue 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
✨ Vérifier la solution et pratiquer

Conception de la fonction main

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.
✨ Vérifier la solution et pratiquer

Conception de 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;
}
✨ Vérifier la solution et pratiquer

Conception de 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 structure stat. 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);
}
✨ Vérifier la solution et pratiquer

Conception de 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);
}
✨ Vérifier la solution et pratiquer

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.

✨ Vérifier la solution et pratiquer

Sommaire

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.