Dateitypstatistik mit C

CCBeginner
Jetzt üben

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

Dieses Kapitel basiert auf den Datei- und Verzeichnis-Schnittstellen von Linux. Dieses Projekt dreht sich um die Natur des Dateisystems und verwendet die lstat-Funktion und Verzeichnisoperationen, um ein Programm zur rekursiven Zählung von Dateitypen zu implementieren. Es bietet eine bequeme Möglichkeit, einen tiefen Einblick in die Zusammensetzung der Dateitypen im Linux-Dateisystem zu gewinnen. Darüber hinaus kann das in diesem Projekt entwickelte Programm zur Zählung von Dateitypen in praktischen Lern- und Arbeitsumgebungen verwendet werden.

👀 Vorschau

$./file_type.
reguläre Dateien = 2, 66,67 %
Verzeichnisse = 1, 33,33 %
block-spezielle Dateien = 0, 0,00 %
char-spezielle Dateien = 0, 0,00 %
FIFOs = 0, 0,00 %
symbolische Links = 0, 0,00 %
Sockets = 0, 0,00 %

🎯 Aufgaben

In diesem Projekt lernen Sie:

  • Wie man ein Programm in C implementiert, das mit Hilfe der Linux-Datei- und Verzeichnis-Schnittstellen die Dateitypen in einem Verzeichnis rekursiv zählt.

🏆 Errungenschaften

Nach Abschluss dieses Projekts können Sie:

  • Die lstat-Funktion verwenden, um Dateiinformationen in Linux zu erhalten.
  • Verzeichnisoperationen wie das Öffnen von Verzeichnissen und das Lesen von Verzeichnis-Einträgen durchführen.
  • Ein Programm erstellen, das die verschiedenen Dateitypen, einschließlich regulärer Dateien, Verzeichnisse, block-spezialer Dateien, char-spezialer Dateien, benanntes Pipes, symbolische Links und Sockets, rekursiv zählt.
  • Die Prozentzahl jedes Dateityps innerhalb eines Verzeichnisses berechnen und anzeigen.

Grundlagen und Erstellung von Projekt-Dateien

Als nächstes werden wir die Schritte von der Konzeption bis zur Implementierung vorstellen, wobei hauptsächlich die folgenden C-Sprachen-Kenntnissepunkte angewendet werden:

  • stat-Struktur und lstat-Funktion, opendir, readdir-Funktionen, dirent-Struktur, Rekursion, Funktionsaufrufe usw.

Im gesamten Programm konstruieren wir Funktionen wie main, myftw, dopath, myfunc und path_alloc.

  • Die myfunc-Funktion durchläuft hauptsächlich und zählt die Dateitypen, die den Kriterien entsprechen.
  • Die dopath-Funktion erhält hauptsächlich rekursiv Pfade und bestimmt, ob es sich um Verzeichnisse oder Dateien handelt.
  • Die myftw-Funktion beginnt mit dem Erhalten der Startadresse und der Größe des Arbeitsspeichers für den Speicher des vollständigen Pfads von path_alloc.
  • Die path_alloc-Funktion weist hauptsächlich Arbeitsspeicher für den Pfad (vollständigen Pfad) zu.

Erstellen Sie in Ihrem ~/project-Verzeichnis eine neue Datei namens file_type.c und öffnen Sie sie in Ihrem bevorzugten Code-Editor.

cd ~/project
touch file_type.c
✨ Lösung prüfen und üben

Entwerfen der main-Funktion

Die Hauptaufgabe der main-Funktion besteht darin, zunächst die Befehlszeilenargumente zu empfangen und ihre Gültigkeit zu überprüfen. Anschließend ruft sie die myftw-Funktion auf, um die Anzahl der verschiedenen Dateitypen zu berechnen. Schließlich berechnet sie den Prozentsatz der Dateitypen und gibt sie aus.

#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 für nicht-verzeichnisbasierte Dateien */
#define FTW_D 2 /* Flag für Verzeichnisdateien */
#define FTW_DNR 3 /* Flag für nicht-lesbare Verzeichnisdateien */
#define FTW_NS 4 /* Flag für Dateien, auf die stat nicht zugreifen kann */

static char *fullpath;
static size_t pathlen;

/* Definiere die Funktion zur Dateibearbeitung */
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;

 // Überprüfe die Eingabegültigkeit
 if (argc!= 2)
 {
  printf("Ungültige Befehlseingabe! \n");
  exit(1);
 }

 /* Berechne die Anzahl der verschiedenen Dateitypen */
 ret = myftw(argv[1], myfunc);

 /* Berechne die Gesamtzahl der Dateien */
 ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock;

 /* Vermeide eine Division durch 0, um die Programmsicherheit zu erhöhen */
 if (ntot == 0)
  ntot = 1;

 /* Drucke den Prozentsatz der verschiedenen Dateitypen */
 printf("reguläre Dateien  = %7ld, %5.2f %%\n", nreg,
  nreg*100.0 / ntot);
 printf("Verzeichnisse    = %7ld, %5.2f %%\n", ndir,
  ndir*100.0 / ntot);
 printf("block-spezielle Dateien  = %7ld, %5.2f %%\n", nblk,
  nblk*100.0 / ntot);
 printf("char-spezielle Dateien   = %7ld, %5.2f %%\n", nchr,
  nchr*100.0 / ntot);
 printf("FIFOs          = %7ld, %5.2f %%\n", nfifo,
  nfifo*100.0 / ntot);
 printf("symbolische Links = %7ld, %5.2f %%\n", nslink,
  nslink*100.0 / ntot);
 printf("Sockets        = %7ld, %5.2f %%\n", nsock,
  nsock*100.0 / ntot);
 exit(ret);
}
  • *fullpath: Wird verwendet, um den vollständigen Pfad der Datei zu speichern.
  • pathlen: Wird verwendet, um die Länge des Dateipfads zu speichern.
  • nreg: Die Anzahl der regulären Dateien.
  • ndir: Die Anzahl der Verzeichnisdateien.
  • nblk: Die Anzahl der block-spezialen Dateien.
  • nchr: Die Anzahl der char-spezialen Dateien.
  • nfifo: Die Anzahl der benannt Pipes.
  • nslink: Die Anzahl der symbolischen Linkdateien.
  • nsock: Die Anzahl der Socketdateien.
  • ntot: Gesamtzahl der Dateien.
✨ Lösung prüfen und üben

Entwerfen der myftw-Funktion

Die Funktion wird verwendet, um pathname zu verarbeiten und in einem globalen Zeichenarray zu speichern, und ruft dann dopath auf. Die Funktion path_alloc wird verwendet, um Speicher zuzuweisen. Es ist wichtig zu beachten, dass fullpath eine globale Variable ist, sodass verschiedene Funktionen sie bequem verwenden können. Anschließend ruft man die dopath-Funktion auf, um den Pfadnamen weiter zu verarbeiten (egal, ob es sich um ein Verzeichnis handelt oder nicht).

static int myftw(char *pathname, Myfunc *func)
{
 /* Weise Speicher für das Zeichenarray zu, um den Pfad zu speichern */
 fullpath = path_alloc(&pathlen);

 /* Wenn der zugewiesene Speicher nicht ausreicht, um den Pfad zu speichern,
    wird realloc verwendet, um erneut Speicher zuzuweisen */
 if (pathlen <= strlen(pathname)) {
  pathlen = strlen(pathname) * 2;
  if ((fullpath = realloc(fullpath, pathlen)) == NULL);
  printf("realloc fehlgeschlagen!\n");
 }

 /* Speichere den Pfadnamen-Parameter im vollständigen Pfad. Beachte: fullpath
    ist eine globale Variable und kann von dopath aufgerufen werden */
 strcpy(fullpath, pathname);

 /* Rufe die dopath-Funktion auf */
 return(dopath(func));
}

/* Zuweisung des Pfadarrays */
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;
}
✨ Lösung prüfen und üben

Entwerfen der dopath-Funktion

  • int lstat(const char *path, struct stat *buf) Wenn die Datei ein symbolischer Link ist, liefert lstat Informationen über den symbolischen Link selbst, während stat Informationen über die Datei liefert, auf die der Link zeigt. Eine hier verwendete Datenstruktur ist die stat-Struktur. Die Definition dieser Struktur lautet wie folgt:
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 ) Funktion: Öffnet ein Verzeichnis und gibt einen leeren Zeiger zurück, wenn es fehlschlägt.

  • struct dirent *readdir(DIR *dir) Funktion: readdir() liefert den nächsten Verzeichniseintrag im Verzeichnisstrom dir. Rückgabewert: Bei Erfolg liefert readdir() einen Zeiger auf den nächsten Verzeichniseintrag. Beim Erreichen des Endes des Verzeichnisstroms liefert readdir() NULL.

/* dopath wird verwendet, um zu bestimmen, ob es sich um ein Verzeichnis handelt, und wählt dann entweder direkt die myfunc-Funktion für die Zählung aufzurufen oder die dopath-Funktion rekursiv aufzurufen. */
static int dopath(Myfunc* func)
{
 struct stat statbuf;
 struct dirent *dirp;
 DIR *dp;
 int ret, n;

 /* Rufe die lstat-Funktion auf, um die stat-Informationen des Pfadnamens zu erhalten. Wenn es fehlschlägt, rufe die func-Funktion auf und übergebe FTW_NS */
 if (lstat(fullpath, &statbuf) < 0)
  return(func(fullpath, &statbuf, FTW_NS));

 /* Überprüfe das st_mode der Datei-stat-Struktur. Wenn es kein Verzeichnis ist, rufe die func-Funktion auf und übergebe FTW_F, und bestimme dann den Dateityp durch myfunc */
 if (S_ISDIR(statbuf.st_mode) == 0)
  return(func(fullpath, &statbuf, FTW_F));

 /* Der letzte Fall ist, dass der Pfadname ein Verzeichnis darstellt. Der normale Rückgabewert von func ist 0, sodass es nach der Ausführung von func nicht zurückkehrt und die func-Funktion rekursiv weiter aufgerufen wird */
 if ((ret = func(fullpath, &statbuf, FTW_D))!= 0)
  return(ret);
 /* Pfadverarbeitung, erweitere die Länge des Pfadraums */
 n = strlen(fullpath);
 if (n + NAME_MAX + 2 > pathlen) {
  pathlen *= 2;
  if ((fullpath = realloc(fullpath, pathlen)) == NULL)
   printf("realloc fehlgeschlagen!\n");
 }
 fullpath[n++] = '/';
 fullpath[n] = 0;

 /* Verarbeite jeden Eintrag im Verzeichnis */
 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;  /* Ignoriere das aktuelle Verzeichnis (.) und das übergeordnete Verzeichnis (..), um eine Endlosschleife zu vermeiden */
  strcpy(&fullpath[n], dirp->d_name); /* Füge den Namen des aktuellen Verzeichniseintrags nach "/" an */
  if ((ret = dopath(func))!= 0) /* Rufe dann dopath mit dem neuen Pfadnamen rekursiv auf */
   break;
 }
 fullpath[n-1] = 0;

 /* Schließe das Verzeichnis */
 if (closedir(dp) < 0)
  printf("kann Verzeichnis %s nicht schließen", fullpath);
 return(ret);
}
✨ Lösung prüfen und üben

Entwerfen der myfunc-Funktion

Der Hauptzweck der myfunc-Funktion besteht darin, den Dateityp basierend auf stat zu bestimmen und zu zählen. S_IFMT ist eine Maske, die verwendet wird, um die st_mode-Flags zu interpretieren.

Es gibt einige Makrodefinitionen, die zur Bestimmung des Dateityps helfen. Dies sind parametrisierte Makros, ähnlich wie Funktionsaufrufe:

  • S_ISBLK: Testet, ob es sich um eine besondere Blockgeräte-Datei handelt.
  • S_ISCHR: Testet, ob es sich um eine besondere Zeichengeräte-Datei handelt.
  • S_ISDIR: Testet, ob es sich um ein Verzeichnis handelt.
  • S_ISFIFO: Testet, ob es sich um eine FIFO-Geräte handelt.
  • S_ISREG: Testet, ob es sich um eine reguläre Datei handelt.
  • S_ISLNK: Testet, ob es sich um einen symbolischen Link handelt.
  • S_ISSOCK: Testet, ob es sich um einen Socket handelt.
static int myfunc(const char *pathname, const struct stat *statptr, int type)
{
 switch (type) {

 /* Behandlung von nicht-verzeichnisbasierten Dateien */
 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("für S_IFDIR für %s", pathname);
  }
  break;

 /* Behandlung von Verzeichnisdateien */
 case FTW_D:
  ndir++;
  break;

 /* Behandlung von nicht-lesbaren Verzeichnissen */
 case FTW_DNR:
  printf("%s Verzeichnis ist nicht lesbar", pathname);
  break;
 case FTW_NS:
  printf("%s Fehler bei stat", pathname);
  break;
 default:
  printf("Typ %d wird nicht erkannt, Pfadname ist %s", type, pathname);
 }
 return(0);
}
✨ Lösung prüfen und üben

Kompilieren und Testen

Führen Sie im Terminal den folgenden Befehl aus, um zu kompilieren und auszuführen:

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.
reguläre Dateien = 2, 66.67 %
Verzeichnisse = 1, 33.33 %
block-spezielle Dateien = 0, 0.00 %
char-spezielle Dateien = 0, 0.00 %
FIFOs = 0, 0.00 %
symbolische Links = 0, 0.00 %
Sockets = 0, 0.00 %

Die Ergebnisse zeigen, dass in dem aktuellen Verzeichnis reguläre Dateien 66,67 % und Verzeichnisse 33,33 % ausmachen.

Wenn Sie die Berechtigungen im Systemverzeichnis zählen möchten, können Sie den Befehl mit sudo ausführen. Auch so gibt es jedoch immer noch einige Dateien, für die die Berechtigungen nicht gezählt werden können.

✨ Lösung prüfen und üben

Zusammenfassung

Durch die Bearbeitung dieses Projekts kann man sein Verständnis des Linux-Dateisystems verbessern. Man lernt, wie man Verzeichnisoperationen durchführt und bekommt einen tieferen Einblick in die stat-Struktur, die Dateiinformationen speichert. Indem man dieses Projekt abschließt, kann man ein praktisches Linux-Tool entwickeln.