Estadísticas de tipo de archivo usando C

CBeginner
Practicar Ahora

Introducción

Este capítulo se basa en las interfaces de archivos y directorios de Linux. Este proyecto gira en torno a la naturaleza del sistema de archivos, utilizando la función lstat y las operaciones de directorio para implementar un programa para contar recursivamente los tipos de archivos. Proporciona una forma conveniente de adquirir una comprensión profunda de la composición de los tipos de archivos en el sistema de archivos de Linux. Además, el programa de conteo de tipos de archivos desarrollado en este proyecto se puede utilizar en entornos de aprendizaje y trabajo prácticos.

👀 Vista previa

$./file_type.
archivos regulares = 2, 66,67 %
directorios = 1, 33,33 %
especial de bloque = 0, 0,00 %
especial de carácter = 0, 0,00 %
FIFOs = 0, 0,00 %
vínculos simbólicos = 0, 0,00 %
sockets = 0, 0,00 %

🎯 Tareas

En este proyecto, aprenderá:

  • Cómo implementar un programa en C que cuente recursivamente los tipos de archivos en un directorio utilizando las interfaces de archivos y directorios de Linux.

🏆 Logros

Después de completar este proyecto, podrá:

  • Utilizar la función lstat para obtener información de archivos en Linux.
  • Realizar operaciones de directorio como abrir directorios y leer entradas de directorio.
  • Crear un programa que cuente recursivamente diferentes tipos de archivos, incluyendo archivos regulares, directorios, archivos especiales de bloque, archivos especiales de carácter, tuberías nombradas, vínculos simbólicos y sockets.
  • Calcular y mostrar el porcentaje de cada tipo de archivo dentro de un directorio.

Conocimientos básicos y creación de archivos del proyecto

A continuación, presentaremos los pasos desde la concepción hasta la implementación, aplicando principalmente los siguientes puntos de conocimiento del lenguaje C:

  • Estructura stat y función lstat, funciones opendir, readdir, estructura dirent, recursividad, llamadas a funciones, etc.

En todo el programa, construimos funciones como main, myftw, dopath, myfunc y path_alloc.

  • La función myfunc principalmente recorre y cuenta los tipos de archivos que cumplen con los criterios.
  • La función dopath principalmente obtiene recursivamente las rutas y determina si son directorios o archivos.
  • La función myftw comienza a partir de obtener la dirección de inicio y el tamaño del espacio de memoria para almacenar la ruta completa a partir de path_alloc.
  • La función path_alloc principalmente asigna espacio de memoria para la ruta (ruta completa).

Crea un nuevo archivo llamado file_type.c en el directorio ~/project y dile que lo abra en tu editor de código preferido.

cd ~/project
touch file_type.c

Diseña la función principal

La función principal de la función main es primero recibir los argumentos de línea de comandos y comprobar su validez. Luego llama a la función myftw para calcular la cantidad de varios tipos de archivos. Finalmente, calcula el porcentaje de los tipos de archivos y los imprime.

#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 /* Bandera para archivos no directorio */
#define FTW_D 2 /* Bandera para archivos directorio */
#define FTW_DNR 3 /* Bandera para archivos directorio no legibles */
#define FTW_NS 4 /* Bandera para archivos que no se pueden acceder con stat */

static char *fullpath;
static size_t pathlen;

/* Define la función para manejar archivos */
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;

 // Realiza la comprobación de validez de entrada
 if (argc!= 2)
 {
  printf("Entrada de comando no válida! \n");
  exit(1);
 }

 /* Calcula la cantidad de varios tipos de archivos */
 ret = myftw(argv[1], myfunc);

 /* Calcula el número total de archivos */
 ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock;

 /* Evita la división por 0 para mejorar la estabilidad del programa */
 if (ntot == 0)
  ntot = 1;

 /* Imprime el porcentaje de varios tipos de archivos */
 printf("archivos regulares  = %7ld, %5.2f %%\n", nreg,
  nreg*100.0 / ntot);
 printf("directorios    = %7ld, %5.2f %%\n", ndir,
  ndir*100.0 / ntot);
 printf("especial de bloque  = %7ld, %5.2f %%\n", nblk,
  nblk*100.0 / ntot);
 printf("especial de carácter   = %7ld, %5.2f %%\n", nchr,
  nchr*100.0 / ntot);
 printf("FIFOs          = %7ld, %5.2f %%\n", nfifo,
  nfifo*100.0 / ntot);
 printf("vínculos simbólicos = %7ld, %5.2f %%\n", nslink,
  nslink*100.0 / ntot);
 printf("sockets        = %7ld, %5.2f %%\n", nsock,
  nsock*100.0 / ntot);
 exit(ret);
}
  • *fullpath: Utilizado para almacenar la ruta completa del archivo.
  • pathlen: Utilizado para almacenar la longitud de la ruta del archivo.
  • nreg: La cantidad de archivos regulares.
  • ndir: La cantidad de archivos directorio.
  • nblk: La cantidad de archivos especiales de bloque.
  • nchr: La cantidad de archivos especiales de carácter.
  • nfifo: La cantidad de tuberías nombradas.
  • nslink: La cantidad de archivos de vínculo simbólico.
  • nsock: La cantidad de archivos de socket.
  • ntot: Número total de archivos.

Diseña la función myftw

La función se utiliza para manejar pathname y guardarlo en un arreglo de caracteres global, y luego llamar a dopath. La función path_alloc se utiliza para asignar espacio, y es importante tener en cuenta que fullpath es una variable global, por lo que diferentes funciones pueden utilizarla convenientemente. Luego, llama a la función dopath para procesar aún más el nombre de ruta (ya sea un directorio o no).

static int myftw(char *pathname, Myfunc *func)
{
 /* Asigna espacio para el arreglo de cadenas para guardar la ruta */
 fullpath = path_alloc(&pathlen);

 /* Si el espacio asignado no es suficiente para guardar la ruta, utiliza realloc para reasignar */
 if (pathlen <= strlen(pathname)) {
  pathlen = strlen(pathname) * 2;
  if ((fullpath = realloc(fullpath, pathlen)) == NULL);
  printf("realloc fallido!\n");
 }

 /* Guarda el parámetro pathname en la ruta completa. Presta atención: fullpath es una variable global
    y puede ser llamada por dopath */
 strcpy(fullpath, pathname);

 /* Llama a la función dopath */
 return(dopath(func));
}

/* Asignación de arreglo de ruta */
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;
}

Diseña la función 'dopath'

  • int lstat(const char *path, struct stat *buf) Cuando el archivo es un enlace simbólico, lstat devuelve información sobre el enlace simbólico en sí mismo, mientras que stat devuelve información sobre el archivo al que apunta el enlace. Una estructura de datos utilizada aquí es la estructura stat. La definición de esta estructura es la siguiente:
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 ) Función: abre un directorio, devolviendo un puntero nulo en caso de error.

  • struct dirent *readdir(DIR *dir) Función: readdir() devuelve la siguiente entrada de directorio en el flujo de directorio dir. Valor de retorno: En caso de éxito, readdir() devuelve un puntero a la siguiente entrada de directorio. Al llegar al final del flujo de directorio, readdir() devuelve NULL.

/* dopath se utiliza para determinar si es un directorio, y luego elegir si directamente entrar en la función myfunc para contar, o llamar recursivamente a la función dopath. */
static int dopath(Myfunc* func)
{
 struct stat statbuf;
 struct dirent *dirp;
 DIR *dp;
 int ret, n;

 /* Llama a la función lstat para obtener la información stat del pathname. Si falla, llama a la función func y pasa FTW_NS */
 if (lstat(fullpath, &statbuf) < 0)
  return(func(fullpath, &statbuf, FTW_NS));

 /* Verifica el st_mode de la estructura stat del archivo. Si no es un directorio, llama a la función func y pasa FTW_F, y luego determina el tipo de archivo por myfunc */
 if (S_ISDIR(statbuf.st_mode) == 0)
  return(func(fullpath, &statbuf, FTW_F));

 /* El último caso es que el pathname representa un directorio. El valor de retorno normal de func es 0, por lo que después de ejecutar func no devolverá y continuará llamando recursivamente a func */
 if ((ret = func(fullpath, &statbuf, FTW_D))!= 0)
  return(ret);
 /* Procesamiento del path, amplía la longitud del espacio del path */
 n = strlen(fullpath);
 if (n + NAME_MAX + 2 > pathlen) {
  pathlen *= 2;
  if ((fullpath = realloc(fullpath, pathlen)) == NULL)
   printf("realloc fallido!\n");
 }
 fullpath[n++] = '/';
 fullpath[n] = 0;

 /* Maneja cada entrada en el directorio */
 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;  /* Ignora el directorio actual (.) y el directorio padre (..) para evitar un bucle infinito */
  strcpy(&fullpath[n], dirp->d_name); /* Añade el nombre de la entrada actual de directorio después de "/" */
  if ((ret = dopath(func))!= 0) /* Luego llama recursivamente a dopath con el nuevo pathname */
   break;
 }
 fullpath[n-1] = 0;

 /* Cierra el directorio */
 if (closedir(dp) < 0)
  printf("no se puede cerrar el directorio %s", fullpath);
 return(ret);
}

Diseña la función myfunc

El principal propósito de la función myfunc es determinar el tipo de archivo basado en stat y contarlos. S_IFMT es una máscara utilizada para interpretar las banderas de st_mode.

Hay algunas definiciones de macro utilizadas para ayudar a determinar el tipo de archivo. Estas son macros parametrizadas, similares a llamadas a funciones:

  • S_ISBLK: Comprueba si es un archivo de dispositivo de bloque especial.
  • S_ISCHR: Comprueba si es un archivo de dispositivo de carácter especial.
  • S_ISDIR: Comprueba si es un directorio.
  • S_ISFIFO: Comprueba si es un dispositivo FIFO.
  • S_ISREG: Comprueba si es un archivo regular.
  • S_ISLNK: Comprueba si es un enlace simbólico.
  • S_ISSOCK: Comprueba si es un socket.
static int myfunc(const char *pathname, const struct stat *statptr, int type)
{
 switch (type) {

 /* Manejo de archivos no directorio */
 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("para S_IFDIR para %s", pathname);
  }
  break;

 /* Manejo de archivos directorio */
 case FTW_D:
  ndir++;
  break;

 /* Manejo de directorios no legibles */
 case FTW_DNR:
  printf("%s directorio no es legible", pathname);
  break;
 case FTW_NS:
  printf("%s error en stat", pathname);
  break;
 default:
  printf("Tipo %d no es reconocido, pathname es %s", type, pathname);
 }
 return(0);
}

Compila y prueba

Ingresa el siguiente comando en la terminal para compilar y ejecutar:

cd ~/proyecto
gcc -o file_type file_type.c
./file_type.
labex:proyecto/ $ ls
file_type file_type.c

labex:proyecto/ $ gcc -o file_type file_type.c

labex:proyecto/ $./file_type.
archivos regulares = 2, 66.67 %
directorios = 1, 33.33 %
especial de bloque = 0, 0.00 %
especial de carácter = 0, 0.00 %
FIFOs = 0, 0.00 %
vínculos simbólicos = 0, 0.00 %
sockets = 0, 0.00 %

Los resultados muestran que en el directorio actual, los archivos regulares representan el 66.67% y los directorios el 33.33%.

Si necesitas contar los permisos en el directorio del sistema, puedes usar sudo antes de ejecutar el comando, incluso así, todavía hay algunos archivos para los cuales no se pueden contar los permisos.

Resumen

A través de la capacitación de este proyecto, se puede mejorar la comprensión del sistema de archivos de Linux. Se aprenderá cómo realizar operaciones de directorio y se obtendrá una comprensión más profunda de la estructura stat que almacena información de archivos. Al completar este proyecto, se puede desarrollar una herramienta práctica de Linux.

✨ Revisar Solución y Practicar✨ Revisar Solución y Practicar✨ Revisar Solución y Practicar✨ Revisar Solución y Practicar✨ Revisar Solución y Practicar✨ Revisar Solución y Practicar