Introdução
Este capítulo é baseado nas interfaces de arquivos e diretórios do Linux. Este projeto gira em torno da natureza do sistema de arquivos, utilizando a função lstat e operações de diretório para implementar um programa que conta recursivamente os tipos de arquivos. Ele fornece uma maneira conveniente de obter uma compreensão profunda da composição dos tipos de arquivos no sistema de arquivos Linux. Além disso, o programa de contagem de tipos de arquivos desenvolvido neste projeto pode ser usado em ambientes práticos de aprendizado e trabalho.
👀 Visualização
$ ./file_type .
regular files = 2, 66.67 %
directories = 1, 33.33 %
block special = 0, 0.00 %
char special = 0, 0.00 %
FIFOs = 0, 0.00 %
symbolic links = 0, 0.00 %
sockets = 0, 0.00 %
🎯 Tarefas
Neste projeto, você aprenderá:
- Como implementar um programa em C que conta recursivamente os tipos de arquivos em um diretório usando as interfaces de arquivos e diretórios do Linux.
🏆 Conquistas
Após concluir este projeto, você será capaz de:
- Usar a função
lstatpara obter informações sobre arquivos no Linux. - Realizar operações de diretório, como abrir diretórios e ler entradas de diretório.
- Criar um programa que conta recursivamente diferentes tipos de arquivos, incluindo arquivos regulares, diretórios, arquivos especiais de bloco, arquivos especiais de caractere, pipes nomeados, links simbólicos e sockets.
- Calcular e exibir a porcentagem de cada tipo de arquivo dentro de um diretório.
Conhecimentos Básicos e Criação dos Arquivos do Projeto
Em seguida, apresentaremos as etapas da concepção à implementação, aplicando principalmente os seguintes pontos de conhecimento da linguagem C:
- Estrutura
state funçãolstat, funçõesopendir,readdir, estruturadirent, recursão, chamadas de função, etc.
Em todo o programa, construímos funções como main, myftw, dopath, myfunc e path_alloc.
- A função
myfuncpercorre e conta principalmente os tipos de arquivos que atendem aos critérios. - A função
dopathobtém recursivamente caminhos e determina se são diretórios ou arquivos. - A função
myftwcomeça obtendo o endereço inicial e o tamanho do espaço de memória para armazenar o caminho completo depath_alloc. - A função
path_allocaloca principalmente espaço de memória para o caminho (caminho completo).
Crie um novo arquivo chamado file_type.c no diretório ~/project e abra-o no seu editor de código preferido.
cd ~/project
touch file_type.c
Projetar a função principal (main)
A principal função da função main é, primeiramente, receber os argumentos da linha de comando e verificar sua validade. Em seguida, ela chama a função myftw para calcular o número de vários tipos de arquivos. Finalmente, ela calcula a porcentagem dos tipos de arquivos e os exibe.
#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 /* Flag for non-directory files */
#define FTW_D 2 /* Flag for directory files */
#define FTW_DNR 3 /* Flag for unreadable directory files */
#define FTW_NS 4 /* Flag for files that cannot be accessed by stat */
static char *fullpath;
static size_t pathlen;
/* Define the function for handling files */
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;
// Perform input validity check
if (argc != 2)
{
printf("Invalid command input! \n");
exit(1);
}
/* Calculate the number of various types of files */
ret = myftw(argv[1], myfunc);
/* Calculate the total number of files */
ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock;
/* Avoid division by 0 to improve program stability */
if (ntot == 0)
ntot = 1;
/* Print the percentage of various types of files */
printf("regular files = %7ld, %5.2f %%\n", nreg,
nreg*100.0 / ntot);
printf("directories = %7ld, %5.2f %%\n", ndir,
ndir*100.0 / ntot);
printf("block special = %7ld, %5.2f %%\n", nblk,
nblk*100.0 / ntot);
printf("char special = %7ld, %5.2f %%\n", nchr,
nchr*100.0 / ntot);
printf("FIFOs = %7ld, %5.2f %%\n", nfifo,
nfifo*100.0 / ntot);
printf("symbolic links = %7ld, %5.2f %%\n", nslink,
nslink*100.0 / ntot);
printf("sockets = %7ld, %5.2f %%\n", nsock,
nsock*100.0 / ntot);
exit(ret);
}
*fullpath: Usado para armazenar o caminho completo do arquivo.pathlen: Usado para armazenar o comprimento do caminho do arquivo.nreg: O número de arquivos regulares.ndir: O número de arquivos de diretório.nblk: O número de arquivos especiais de bloco.nchr: O número de arquivos especiais de caractere.nfifo: O número de pipes nomeados.nslink: O número de arquivos de link simbólico.nsock: O número de arquivos de socket.ntot: Número total de arquivos.
Projetar a função myftw
A função é usada para lidar com pathname e salvá-lo em um array de caracteres global, e então chamar dopath. A função path_alloc é usada para alocar espaço, e é importante notar que fullpath é uma variável global, para que diferentes funções possam usá-la convenientemente. Em seguida, chama-se a função dopath para processar ainda mais o pathname (se é um diretório ou não).
static int myftw(char *pathname, Myfunc *func)
{
/* Allocate space for the string array to save the path */
fullpath = path_alloc(&pathlen);
/* If the allocated space is not enough to save the path, use realloc to reallocate */
if (pathlen <= strlen(pathname)) {
pathlen = strlen(pathname) * 2;
if ((fullpath = realloc(fullpath, pathlen)) == NULL);
printf("realloc failed!\n");
}
/* Save the pathname parameter in the full path. Pay attention: fullpath is a global variable
and can be called by dopath */
strcpy(fullpath, pathname);
/* Call the dopath function */
return(dopath(func));
}
/* Path array allocation */
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;
}
Projetar a função 'dopath'
int lstat(const char *path, struct stat *buf)Quando o arquivo é um link simbólico,lstatretorna informações sobre o próprio link simbólico, enquantostatretorna informações sobre o arquivo para o qual o link aponta. Uma estrutura de dados usada aqui é a estruturastat. A definição desta estrutura é a seguinte:
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 )Função: abre um diretório, retornando um ponteiro vazio em caso de falha.struct dirent *readdir(DIR *dir)Função:readdir()retorna a próxima entrada de diretório no fluxo de diretóriodir. Valor de retorno: Em caso de sucesso,readdir()retorna um ponteiro para a próxima entrada de diretório. Ao atingir o final do fluxo de diretório,readdir()retorna NULL.
/* dopath is used to determine if it is a directory, and then choose whether to directly enter the myfunc function for counting, or recursively call the dopath function. */
static int dopath(Myfunc* func)
{
struct stat statbuf;
struct dirent *dirp;
DIR *dp;
int ret, n;
/* Call the lstat function to obtain the stat information of the pathname. If it fails, call the func function and pass FTW_NS */
if (lstat(fullpath, &statbuf) < 0)
return(func(fullpath, &statbuf, FTW_NS));
/* Check the st_mode of the file stat structure. If it is not a directory, call the func function and pass FTW_F, and then determine the file type by myfunc */
if (S_ISDIR(statbuf.st_mode) == 0)
return(func(fullpath, &statbuf, FTW_F));
/* The last case is that the pathname represents a directory. The normal return value of func is 0, so after executing func, it will not return and will continue to recursively call func */
if ((ret = func(fullpath, &statbuf, FTW_D)) != 0)
return(ret);
/* Path processing, expand the length of the path space */
n = strlen(fullpath);
if (n + NAME_MAX + 2 > pathlen) {
pathlen *= 2;
if ((fullpath = realloc(fullpath, pathlen)) == NULL)
printf("realloc failed!\n");
}
fullpath[n++] = '/';
fullpath[n] = 0;
/* Handle each entry in the directory */
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; /* Ignore the current directory (.) and the parent directory (..) to avoid infinite loop */
strcpy(&fullpath[n], dirp->d_name); /* Append the name of the current directory entry after "/" */
if ((ret = dopath(func)) != 0) /* Then recursively call dopath with the new pathname */
break;
}
fullpath[n-1] = 0;
/* Close the directory */
if (closedir(dp) < 0)
printf("can't close directory %s", fullpath);
return(ret);
}
Projetar a função myfunc
O principal objetivo da função myfunc é determinar o tipo de arquivo com base em stat e contá-los. S_IFMT é uma máscara usada para interpretar as flags st_mode.
Existem algumas definições de macro usadas para ajudar a determinar o tipo de arquivo. Estas são macros parametrizadas, semelhantes a chamadas de função:
S_ISBLK: Testa se é um arquivo de dispositivo de bloco especial.S_ISCHR: Testa se é um arquivo de dispositivo de caractere especial.S_ISDIR: Testa se é um diretório.S_ISFIFO: Testa se é um dispositivo FIFO.S_ISREG: Testa se é um arquivo regular.S_ISLNK: Testa se é um link simbólico.S_ISSOCK: Testa se é um socket.
static int myfunc(const char *pathname, const struct stat *statptr, int type)
{
switch (type) {
/* Handling for non-directory files */
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;
/* Handling for directory files */
case FTW_D:
ndir++;
break;
/* Handling for unreadable directories */
case FTW_DNR:
printf("%s directory is unreadable", pathname);
break;
case FTW_NS:
printf("%s error in stat", pathname);
break;
default:
printf("Type %d is unrecognized, pathname is %s", type, pathname);
}
return(0);
}
Compilar e testar
Insira o seguinte comando no terminal para compilar e executar:
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 .
regular files = 2, 66.67 %
directories = 1, 33.33 %
block special = 0, 0.00 %
char special = 0, 0.00 %
FIFOs = 0, 0.00 %
symbolic links = 0, 0.00 %
sockets = 0, 0.00 %
Os resultados mostram que, no diretório atual, os arquivos regulares representam 66,67% e os diretórios representam 33,33%.
Se você precisar contar as permissões no diretório do sistema, pode usar sudo antes de executar o comando; mesmo assim, ainda existem alguns arquivos para os quais as permissões não podem ser contadas.
Resumo
Através do treinamento deste projeto, pode-se aprimorar a compreensão do sistema de arquivos Linux. Aprenderão como realizar operações de diretório e obter uma compreensão mais profunda da estrutura stat que armazena informações sobre arquivos. Ao concluir este projeto, pode-se desenvolver uma ferramenta Linux prática.



