2013-06-25 1 views
1

Au chapitre 4 du livre "Programmation avancée dans l'environnement Unix", qui couvre les fichiers et les répertoires, il y a un exemple de code qui ressemble à la commande ftw une hiérarchie de fichiers. Il utilise un pointeur vers un chemin de fichier absolu, ainsi qu'une fonction récursive avec un rappel pour parcourir le répertoire, en utilisant les appels à opendir() et readdir() dans le processus.Traversée de répertoire avec chdir() au lieu de chemins absolus

Il existe un exercice dans lequel les lecteurs sont invités à utiliser chdir() et les noms de fichiers au lieu d'utiliser les chemins absolus pour accomplir la même tâche et comparer les heures des deux programmes. J'ai écrit un programme en utilisant chdir() et n'a pas remarqué de différence dans le temps. Est-ce prévu? J'aurais pensé que l'appel supplémentaire à chdir() ajouterait un peu de frais généraux. Est-ce peut-être un appel relativement trivial? Toute idée serait appréciée.

est ici la fonction récursive en utilisant des chemins absolus:

static int     /* we return whatever func() returns */ 
dopath(Myfunc* func) 
{ 
    struct stat  statbuf; 
    struct dirent *dirp; 
    DIR    *dp; 
    int    ret; 
    char   *ptr; 

    if (lstat(fullpath, &statbuf) < 0) /* stat error */ 
     return(func(fullpath, &statbuf, FTW_NS)); 
    if (S_ISDIR(statbuf.st_mode) == 0) /* not a directory */ 
     return(func(fullpath, &statbuf, FTW_F)); 

    /* 
     * It's a directory. First call func() for the directory, 
     * then process each filename in the directory. 
     */ 
    if ((ret = func(fullpath, &statbuf, FTW_D)) != 0) 
     return(ret); 

    ptr = fullpath + strlen(fullpath);  /* point to end of fullpath */ 
    *ptr++ = '/'; 
    *ptr = 0; 

    if ((dp = opendir(fullpath)) == NULL)  /* can't read directory */ 
     return(func(fullpath, &statbuf, FTW_DNR)); 

    while ((dirp = readdir(dp)) != NULL) { 
     if (strcmp(dirp->d_name, ".") == 0 || 
      strcmp(dirp->d_name, "..") == 0) 
       continue;  /* ignore dot and dot-dot */ 

     strcpy(ptr, dirp->d_name); /* append name after slash */ 

     if ((ret = dopath(func)) != 0)   /* recursive */ 
       break; /* time to leave */ 
    } 
    ptr[-1] = 0; /* erase everything from slash onwards */ 

    if (closedir(dp) < 0) 
     err_ret("can't close directory %s", fullpath); 

    return(ret); 
} 

Et voici la fonction avec mes changements:

static int     /* we return whatever func() returns */ 
dopath(Myfunc* func, char* path) 
{ 
    struct stat  statbuf; 
    struct dirent *dirp; 
    DIR    *dp; 
    int    ret; 

    if (lstat(path, &statbuf) < 0) /* stat error */ 
     return(func(path, &statbuf, FTW_NS)); 
    if (S_ISDIR(statbuf.st_mode) == 0) /* not a directory */ 
     return(func(path, &statbuf, FTW_F)); 

/* 
* It's a directory. First call func() for the directory, 
* then process each filename in the directory. 
*/ 
    if ((ret = func(path, &statbuf, FTW_D)) != 0) 
     return(ret); 

    if (chdir(path) < 0) 
     return(func(path, &statbuf, FTW_DNR)); 

    if ((dp = opendir(".")) == NULL)  /* can't read directory */ 
     return(func(path, &statbuf, FTW_DNR)); 

    while ((dirp = readdir(dp)) != NULL) { 
     if (strcmp(dirp->d_name, ".") == 0 || 
      strcmp(dirp->d_name, "..") == 0) 
       continue;  /* ignore dot and dot-dot */ 

     if ((ret = dopath(func, dirp->d_name)) != 0)   /* recursive */ 
       break; /* time to leave */ 
    } 
    if (chdir("..") < 0) 
     err_ret("can't go up directory"); 

    if (closedir(dp) < 0) 
     err_ret("can't close directory %s", fullpath); 

    return(ret); 
} 

Répondre

1

Je ne pense pas que vous devriez vous attendre une différence de performance de temps considérable entre l'absolu version du chemin et la version chdir(). Au contraire, les avantages et les inconvénients des deux versions sont les suivantes:

  • La version complète de chemin pourrait ne pas être en mesure de traverser les structures de répertoires très profondes parce que la longueur du chemin complet dépasse finalement PATH_MAX. La version chdir() n'a pas ce problème.
  • La version chdir() manipule le pwd, ce qui est généralement considéré comme une mauvaise pratique si vous pouvez l'éviter: il n'est pas adapté aux threads, et l'utilisateur final peut s'attendre à ce qu'il soit laissé seul. Par exemple, les noms de fichiers donnés sur la ligne de commande et utilisés par une partie différente du programme peuvent être relatifs à ce que l'utilisateur pensait être le pwd, qui se casse lorsque vous le changez.
  • La version chdir() peut devenir incontrôlable lors d'une sauvegarde vers un répertoire supérieur (chdir("..")) si des précautions spéciales ne sont pas prises et si la structure du répertoire change pendant le parcours. Là encore, la version complète du chemin peut se casser différemment dans ces circonstances ...

La famille de fonctions openat() disponibles sur les systèmes POSIX modernes offre le meilleur des deux mondes. Si ces fonctions sont disponibles, openat() ainsi que fdopendir(), fstatat(), etc ... constituent une très bonne implémentation de la navigation dans les répertoires.

+0

Merci pour la perspicacité et les autres fonctions! – rsa

Questions connexes