Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

 << zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch für Linux, Unix und Windows
– 2., aktualisierte und erweiterte Auflage 2006
Buch: C von A bis Z

C von A bis Z
1.116 S., mit CD, Referenzkarte, 39,90 Euro
Galileo Computing
ISBN 3-89842-643-2
gp Kapitel 19 Attribute von Dateien und Arbeiten mit Verzeichnissen (nicht ANSI C)
  gp 19.1 Attribute einer Datei ermitteln – stat()
    gp 19.1.1 stat() – st_mode
    gp 19.1.2 stat() – st_size
    gp 19.1.3 stat() – st_atime, st_mtime und st_ctime
    gp 19.1.4 stat() – st_gid und st_uid
    gp 19.1.5 stat() – st_nlink, st_ino
    gp 19.1.6 stat() – st_dev, st_rdev
  gp 19.2 Prüfen des Zugriffsrechts – access
  gp 19.3 Verzeichnis-Funktionen
    gp 19.3.1 Verzeichnis erstellen, löschen und wechseln – mkdir, rmdir und chdir
    gp 19.3.2 Wechseln in das Arbeitsverzeichnis – getcwd
    gp 19.3.3 Verzeichnisse öffnen, lesen und schließen – opendir, readdir und closedir


Galileo Computing - Zum Seitenanfang

19.3 Verzeichnis-Funktionen  downtop

Bei den bisherigen ANSI C-Funktionen konnte es Ihnen egal sein, wie ein Dateisystem aufgebaut ist. Es gibt zwar einige systemabhängige Faktoren, die zu beachten sind (bspw. das Trennzeichen von Verzeichnisnamen), aber meistens sind diese Funktionen so universell implementiert, dass es dennoch nicht zu Problemen kommt. Bei einem Zugriff auf Verzeichnisse ist es leider nicht mehr so einfach. Hierbei werden meist POSIX-konforme Funktionen verwendet, welche vorwiegend in der UNIX-Welt beheimatet sind. Keine Sorge, auch MS-Windows-Anwender können diese Funktionen nutzen. In vielen Compilern unter diesem System sind diese Funktionen integriert.


Galileo Computing - Zum Seitenanfang

19.3.1 Verzeichnis erstellen, löschen und wechseln – mkdir, rmdir und chdir  downtop

#include <sys/types.h>  /* Linux/UNIX */
#include <sys/stat.h>   /* Linux/UNIX */
#include <dir.h>        /* MS-DOS/WIN */
int mkdir(const char *pfad, [int modus]);

Mit der Funktion mkdir() wird ein neues Verzeichnis mit dem Namen pfad angelegt. Zusätzlich werden in dem neuen Verzeichnis automatisch auch das Arbeitsverzeichnis (Working Directory) (.) und das Eltern-Verzeichnis (Parent Directory) (..) mit angelegt. Die Zugriffsrechte können über modus vergeben werden. Dies gilt aber nur für Linux/UNIX und nicht für Windows/MS-DOS. Die Modi unter Linux/UNIX entnehmen Sie bitte der man-Page von chmod().

Hierzu ein Listing, mit dem ein neues Verzeichnis erstellt wird.

/* create_dir.c */
#ifdef __unix__
   #include <sys/types.h>
   #include <sys/stat.h>
   #define MODUS ,0711)
#elif __WIN32__ || _MS_DOS_
    #include <dir.h>
    #define MODUS )
#else
    #include <direct.h>  /* Visual C++ */
    #define MODUS )
#endif
#include <stdio.h>
#include <stdlib.h>
int main(void) {
   char pfadname[200];
   printf("Wie soll der neue Ordner heissen: ");
   scanf("%199s",pfadname);
   if(mkdir(pfadname MODUS == –1) /*Nicht schön, aber portabler*/
      printf("Konnte kein neues Verzeichnis erstellen\n");
   else
      printf("Neues Verzeichnis namens %s erstellt\n",pfadname);
   return EXIT_SUCCESS;
}

Wurde das Programm ausgeführt, sollte sich im benannten Verzeichnis ein neuer Ordner mit dem eingegebenen Namen befinden. Unter Linux/UNIX muss außerdem beachtet werden, dass für den modus auch die Ausführrechte (execute-Bits) gesetzt sind, um auch Zugriff auf das neue Verzeichnis zu haben.

Sofern versucht wird, ein Verzeichnis zu erstellen, welches bereits existiert, wird dies fehlschlagen. errno wird dann auf einen entsprechenden Wert gesetzt (EEXIST).


Hinweis   Es sollte hierbei nicht unerwähnt bleiben, dass Sie unter Linux/UNIX nicht einfach die Zugriffsrechte für ein neues Verzeichnis bzw. eine neue Datei vergeben können, wie es Ihnen gerade passt. Sie sind dabei von einer gewissen Bit-Einschränkungsmaske abhängig, welche Sie allerdings mit der Funktion umask() verändern können. Dies ist allerdings sehr systemspezifisch und daher sei auf mein anderes Buch »Linux-UNIX-Programmierung« hingewiesen, welches Sie auch zum Probelesen auf meiner Homepage vorfinden.


Als Nächstes soll in das eben erstellte Verzeichnis gewechselt werden. Dies gelingt mit der Funktion chdir(). Die Syntax von chdir():

#include <unistd.h> /* Linux/UNIX */
#include <dir.h>    /* MS-DOS/WIN */
int chdir(const char *pfad);

Mit chdir() wird in das Arbeitsverzeichnis, welches jedes ablaufende Programm besitzt, gewechselt. Bei einem Fehler gibt diese Funktion –1 zurück, ansonsten 0. In dem folgenden Listing wird erst ein neues Verzeichnis erstellt, danach wird mit chdir() in das erstellte Verzeichnis gewechselt und darin eine Textdatei erzeugt.

/* change_dir.c */
#ifdef __linux__
   #include <sys/types.h>
   #include <sys/stat.h>
   #include <unistd.h>
   #define MODUS ,0711)
#elif _WIN32__ || _MS_DOS_
    #include <dir.h>
    #define MODUS )
#else
    #include <direct.h>
    #define MODUS )
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main(void) {
   char pfadname[200];
   printf("Wie soll der neue Ordner heissen : ");
   scanf("%199s",pfadname);
   if(mkdir(pfadname MODUS == –1)
      printf("Konnte kein neues Verzeichnis erstellen\n");
   else {
      printf("Neues Verzeichnis namens %s erstellt\n", pfadname);
      printf(" --> (%s)\n", strerror(errno));
   }
   /* Jetzt wollen wir in das neue Verzeichnis wechseln */
   if(chdir(pfadname) == –1) {
      printf("Konnte nicht in das Verzeichnis wechseln\n");
      return EXIT_FAILURE;
   }
   else
      printf("Erfolgreich nach %s gewechselt!\n", pfadname);
   /* testfile im Verzeichnis erstellen*/
   fopen("testfile", "w");
   return EXIT_SUCCESS;
}

Jetzt sollte sich in dem eben erzeugten Verzeichnis eine Datei namens »testfile« befinden. Es dürfte Ihnen aufgefallen sein, dass wenn sich das Programm beendet, es automatisch wieder in das Verzeichnis des Elternprozesses zurückwechselt.

Wenn Sie mehrmals in einem Programm Verzeichnisse erstellen müssen und in diese wechseln, schreiben Sie besser eine Funktion wie z.B.:

int makedir(char *dir) {
   if(mkdir(dir, 0755) != –1) /*Windos/MS-DOS ohne 0755*/
      if(chdir(dir) != –1)
         return OK;
   return ERROR;
}

Wollen Sie ein Verzeichnis wieder löschen, können Sie die Funktion rmdir() verwenden. Die Syntax von rmdir() lautet:

#include <unistd.h>   /* UNIX/Linux */
#include <dir.h>      /* MS-DOS     */
int rmdir(const char *pfad);

Mit rmdir() kann ein Verzeichnis (rmdir = remove directory) gelöscht werden. Unter Linux/UNIX setzt dies allerdings voraus, dass dieses Verzeichnis außer dem (.) und (..) keinen anderen Eintrag mehr beinhaltet. Bei Erfolg gibt diese Funktion 0 und bei einem Fehler –1 zurück.

Dazu soll das Programm, welches eben verwendet wurde, erweitert werden. Das Verzeichnis, welches erstellt, in das gewechselt und in dem eine Datei erzeugt wurde, soll am Ende des Programms wieder gelöscht werden. Hier das Listing:

/* remove_dir.c */
#ifdef __linux__
   #include <sys/types.h>
   #include <sys/stat.h>
   #include <unistd.h>
   #define MODUS ,0711)
#elif _WIN32__ || _MS_DOS_
    #include <dir.h>
    #define MODUS )
#else
    #include <direct.h>
    #define MODUS )
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int makedir(char *dir) {
   if(mkdir(dir MODUS != –1)
      if(chdir(dir) != –1)
         return 0;
   return –1;
}
int main(void) {
   char pfadname[200];
   printf("Wie soll der neue Ordner heissen : ");
   scanf("%199s",pfadname);
   if(makedir(pfadname) == –1) {
      printf("Konnte kein neues Verzeichnis erstellen\n");
      printf(" --> (%s)\n", strerror(errno));
   }
   /* testfile im Verzeichnis erstellen */
   fopen("testfile","w");
   if(rmdir(pfadname) == –1) {
     printf("Konnte Verzeichnis %s nicht löschen!!\n",pfadname);
     printf(" --> (%s)\n", strerror(errno));
   }
   return EXIT_SUCCESS;
}

Unter MS-DOS/Windows wird das Listing problemlos funktionieren. Mit Linux/UNIX kann das Verzeichnis nicht gelöscht werden, da sich dort noch eine Datei befindet. Das Verzeichnis muss also zuvor leer sein. Das vollständige Verzeichnis lässt sich mit folgendem Shellaufruf leeren:

rmdir Verzeichnis | rm -rf Verzeichnis

Im Listing kann dieser Aufruf folgendermaßen eingesetzt werden:

/* remove_dir_unix.c */
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
int main(void) {
   char pfadname[200];
   char deletefiles[200];
   printf("Welchen Ordner wollen Sie löschen : ");
   scanf("%189s",pfadname);
   strcpy(deletefiles,"rm -rf ");
   strcat(deletefiles,pfadname);
   strcat(deletefiles,"/*");
   printf("%s\n",deletefiles);
   system(deletefiles);
   if(rmdir(pfadname) == –1) {
      printf("Konnte Verzeichnis %s nicht löschen!!\n",pfadname);
      printf(" --> (%s)\n", strerror(errno));
   }
   return EXIT_SUCCESS;
}

Galileo Computing - Zum Seitenanfang

19.3.2 Wechseln in das Arbeitsverzeichnis – getcwd  downtop

Mit der Funktion getcwd() lässt sich der Name des Arbeitsverzeichnisses (Working Directory) ermitteln. Hier die Syntax von getcwd():

#include <unistd.h>    /* Linux/UNIX */
#include <dir.h>       /* MS-DOS/WIN */
char *getcwd(char *puffer, int puffergrösse);

Die Funktion schreibt in die Speicheradresse puffer den Pfadnamen des Arbeitsverzeichnisses mit abschließendem '\0'. Mit puffergrösse wird die Größe des Puffers angegeben. Die Funktion gibt bei Erfolg den Pfadnamen des Arbeitsverzeichnisses an puffer zurück oder bei einem Fehler NULL. Hier ein Beispiel, wie diese Funktion verwendet wird:

/* working_D.c */
#ifdef __unix__
    #include <unistd.h>
#elif __WIN32__ || _MS_DOS_
    #include <dir.h>
#else
    #include <direct.h> /* Visual C++ */
#endif
#include <stdio.h>
#include <stdlib.h>
int main(void) {
   char puffer[200];
   if(getcwd(puffer,sizeof(puffer)) == NULL) {
      fprintf(stderr, "Fehler bei getcwd ...\n");
      return EXIT_FAILURE;
   }
   printf("Working-Directory: %s\n", puffer);
   return EXIT_SUCCESS;
}

Für Linux/UNIX gilt außerdem: Wechseln Sie in ein Verzeichnis, welches ein symbolischer Link auf ein anderes Verzeichnis ist, so wird in das Verzeichnis gewechselt, auf das der symbolische Link zeigt.

Ein praktisches Beispiel unter Linux: Der User hat den Namen seines Home-Verzeichnisses vergessen. Er muss aber jetzt wieder in das Verzeichnis wechseln. Welches das ist, kann er herausfinden mit der Eingabe des Shellbefehls env (Environment) oder mit der C-Funktion getenv(). Hier das Listing:

/* go_home.c */
#ifdef __unix__
    #include <unistd.h>
#elif __WIN32__ || _MS_DOS_
    #include <dir.h>
#else
    #include <direct.h> /* Visual C++ */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
   char puffer[200];
   char home[200];
   /* Das Heimatverzeichnis nach home */
   strncpy(home,getenv("HOME"),199);
   /* Working Directory lesen */
   if(getcwd(puffer,sizeof(puffer)) == NULL) {
      fprintf(stderr, "Fehler bei getcwd ...\n");
      return EXIT_FAILURE;
   }
   /* Sind wir schon im Heimatverzeichnis? */
   if(strcmp(home,puffer) == 0)
      printf("Wir sind daheim : %s\n",puffer);
   else { /* Nicht! Dann wechseln wir in Heimatverzeichnis */
      chdir(home);
      /* Der Beweis: */
      printf("back at home: %s \n",
         getcwd(puffer,sizeof(puffer)));
   }
   return EXIT_SUCCESS;
}

Galileo Computing - Zum Seitenanfang

19.3.3 Verzeichnisse öffnen, lesen und schließen – opendir, readdir und closedir  toptop

Um Verzeichnisse zu lesen, ist in der Headerdatei #include <dirent.h> eine interne Struktur namens DIR deklariert. Der Inhalt dieser Struktur ist hier jetzt nicht von Interesse, sondern die folgenden Funktionen, die mit der Struktur arbeiten.


Hinweis   Die folgenden Funktionen sind leider nicht mit dem Microsoft Visual C++ Compiler ausführbar. Dafür wird aber am Ende des Kapitels ein extra Listing angefertigt, das zeigt, wie auch mit dem Visual C++ Compiler Programme erstellt werden können, die ein Verzeichnis auslesen.



Tipp   Wollen Sie die folgenden Beispiele mit der kostenlosen Entwicklungsumgebung Bloodshed Dev-C++ durchführen, müssen Sie im Menü über Projekt Projektoptionen in der Liste Linker die Bibliothek –lmingwex eintragen. Eventuell kann dies aber auch über das Menü Werkzeuge Compiler Optionen in die Liste Linker eingetragen werden. Hierfür genügt aber dann folgender Eintrag: mingwex


opendir() – ein Verzeichnis öffnen

Zuerst die Funktion opendir():

#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *dirname);

Bei Erfolg wird mit dieser Funktion das Verzeichnis dirname geöffnet, auf dessen Adresse dann der DIR-Zeiger verweist. Ansonsten wird bei einem Fehler NULL zurückgegeben.

Der DIR-Zeiger wird jetzt verwendet, um den Inhalt eines Verzeichnisses auszulesen. Dies wird jetzt gleich mit der Funktion readdir() vorgenommen.

readdir() – aus einem Verzeichnis lesen

Hier die Syntax:

#include <sys/types.h>
#include <dirent.h>
struct dirent *readdir(DIR *dir);

Bei einem Fehler gibt diese Funktion ebenfalls NULL zurück. Ansonsten eine Adresse der Struktur dirent, welche Folgendes beinhaltet:

struct dirent {
   long d_ino;      /*i-node Nr. (bei Windows/MS-DOS immer 0)*/
   unsigned short d_reclen;   /* (bei Windows/MS-DOS immer 0)*/
   unsigned short d_namlen;   /* Länge des Namens in d_name  */
   char *d_name;              /* Dateiname mit abschl. '\0'  */
};

In der Praxis kann die Funktion readdir() so verwendet werden:

DIR *dir;
struct dirent *dirzeiger;
/* Verzeichnis öffnen */
if((dir=opendir(dirname)) != NULL)
/* komplettes Verzeichnis Eintrag für Eintrag auslesen */
while((dirzeiger=readdir(dir)) != NULL)
    printf("%s\n",(*dirzeiger).d_name);

Es wird zuerst mit opendir() ein Verzeichnis geöffnet und danach mit readdir() der komplette Inhalt des Verzeichnisses ausgegeben.

rewinddir() – Verzeichnis-Zeiger zurücksetzen auf den Anfang

Mit der Funktion rewinddir() wird der Lesezeiger wieder an den Anfang der Namensliste des Verzeichnisses zurückgesetzt. Die Syntax von rewinddir():

#include <sys/types.h>
#include <dirent.h>
void rewinddir(DIR *dir);

closedir() – Verzeichnis schließen

Am Ende wird dann mit der Funktion closedir() das Verzeichnis geschlossen, welches mit opendir() geöffnet wurde. Bei Erfolg gibt diese Funktion 0 und bei Fehler –1 zurück. Die Syntax lautet:

#include <sys/types.h>
#inlcude <dirent.h>
int closedir(DIR *dir);

Es folgt ein ausführbares Beispiel, welches alle Funktionen in Aktion demonstriert:

/* read_dir.c */
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
   DIR *dir;
   struct dirent *dirzeiger;
   if(argc != 2) {
      fprintf(stderr,"Benutzung : %s Directory\n", argv[0]);
      return EXIT_FAILURE;
   }
   /* Das Verzeichnis öffnen */
   if((dir=opendir(argv[1])) == NULL) {
      fprintf(stderr,"Fehler bei opendir ...\n");
      return EXIT_FAILURE;
   }
   /* Das komplette Verzeichnis auslesen */
   while((dirzeiger=readdir(dir)) != NULL)
      printf("%s\n",(*dirzeiger).d_name);
   /* Lesezeiger wieder schliessen */
   if(closedir(dir) == –1)
      printf("Fehler beim Schliessen von %s\n", argv[1]);
   return EXIT_SUCCESS;
}

Mit diesem Programm wird das vollständige Verzeichnis ausgegeben, welches Sie über die Kommandozeile angeben.

telldir() und seekdir() – Positionierung im Verzeichnis

Auf einigen Systemen gibt es zusätzlich noch die Funktion telldir():

#include <sys/types.h>
#include <sys/dirent.h>
off_t telldir(DIR *dirptr)

Diese Funktion liefert zu einem mit readdir() gelesenen Verzeichnis die Position des Lesezeigers zurück.

Mit der Funktion seekdir() lässt sich die Position des DIR-Zeigers verschieben:

#include <sys/types.h>
#include <sys/dirent.h>
void seekdir(DIR *dirptr, off_t pos)

Damit wird der mit opendir() geöffnete Lesezeiger (dirptr) auf die Position pos gesetzt, welche Sie zuvor mit der Funktion telldir() ermittelt haben. Hierzu noch ein kurzer Ausschnitt dieser beiden Funktionen:

off_t pos;
/* aktuelle Position im Verzeichnis ermitteln */
pos = telldir(dir_ptr);
/* viele Funktionen */
...
/* zur aktuellen Position zurückspringen */
seekdir(dir_ptr, pos);

Probleme mit der Portabilität

Ein Problem bei den Funktionen wie opendir(), readdir() oder closedir() ist, dass diese Funktionen POSIX-konform und aus diesem Grund häufig nicht bei Compilern für MS-Windows implementiert sind. Unter UNIX-artigen Systemen müssen Sie sich wegen dieser Funktionen keine Gedanken machen. Um also unter MS-Windows, genauer unter WIN32, ein vollständiges Verzeichnis auszugeben, müssen Sie auf die Windows-Systemprogrammierung zurückgreifen. Die Windows-Programmierung hier genauer zu erläutern, würde den Rahmen des Kapitels, gar des Buchs, sprengen. Aber zu Anschauungszwecken folgt hier eine portablere Lösung, mit der Sie ein vollständiges Verzeichnis ausgeben lassen können.

/* portabel_readdir.c */
#include <stdio.h>
#include <stdlib.h>
#ifdef __unix__
#include <dirent.h>
#include <sys/types.h>
/* UNIX-Funktion zum Ausgeben des kompl. Verzeichnisses */
void list_dir(const char *path) {
   DIR *dirptr;
   struct dirent *dir;
   if ((dirptr=opendir(path)) == NULL)
      return;
   while((dir=readdir(dirptr)) != NULL)
      printf("%s\n",dir->d_name);
   closedir(dirptr);
}
#elif __WIN32__ || _MSC_VER
#include <windows.h>
/* Win32-Funktion zum Ausgeben des kompl. Verzeichnisses */
void list_dir(const char *path) {
   WIN32_FIND_DATA dir;
   HANDLE fhandle;
   char directory[256];
   /* Unsicher, besser wäre falls vorhanden snprintf() */
   sprintf(directory,"%s\\*.*",path);
   /* Handle auf das Verzeichnis director */
   if ((fhandle=FindFirstFile(directory,&dir)) !=
                             INVALID_HANDLE_VALUE) {
      do {  /* Verzeichnis auslesen */
         printf("%s\n", dir.cFileName);
      } while(FindNextFile(fhandle,&dir));
   }
   FindClose(fhandle);
}
#endif
int main(int argc,char **argv) {
   if (argc < 2)
      list_dir(".");
   else
      list_dir(argv[1]);
   return EXIT_SUCCESS;
}

Bei der Win32-Funktion wurden hier die MS-DOS-ähnlichen Funktionen findfirst() und findnext() verwendet. Die Funktion FindFirstFile() gibt einen Filehandle auf die erste Datei im Verzeichnis zurück. Während FindNextFile() den Handle immer um eine Position weitersetzt, bis keine Dateien mehr im Verzeichnis zum Lesen vorhanden sind. FindClose() schließt den Filehandle wieder.

 << zurück
  
  Zum Katalog
Zum Katalog: C von A bis Z
C von A bis Z
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Shell-Programmierung






 Shell-Programmierung


Zum Katalog: Linux-UNIX-Programmierung






 Linux-UNIX-Programmierung


Zum Katalog: C/C++






 C/C++


Zum Katalog: UML 2.0






 UML 2.0


Zum Katalog: Reguläre Ausdrücke






 Reguläre Ausdrücke


Zum Katalog: Linux






 Linux


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Galileo Press 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de