2013-06-25 43 views
1

在覆蓋文件和目錄的書「UNIX環境高級編程」的第4章,有一個代碼示例,旨在像ftw命令和遍歷文件層次結構。它使用一個指向絕對文件路徑的指針,以及一個帶回調遍歷目錄的遞歸函數,在該過程中使用對opendir()readdir()的調用。目錄遍歷被chdir()而不是使用絕對路徑

有一個練習,讀者被要求使用chdir()和文件名,而不是使用絕對路徑來完成相同的任務,並比較兩個程序的時間。我使用chdir()編寫了一個程序,但沒有注意到時間的差異。這是預期的嗎?我原以爲額外撥打chdir()會增加一些開銷。這可能是一個相對微不足道的電話嗎?任何洞察力將不勝感激。

下面是使用絕對路徑的遞歸函數:

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); 
} 

下面是我的功能改變:

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); 
} 

回答

1

我不認爲你應該想到的絕對之間的大量的時間性能差路徑版本和chdir()版本。更確切地說,是兩個版本的優點和缺點如下:

  • 的完整路徑版本可能無法遍歷非常深的目錄結構,因爲完整路徑的長度,最終超過PATH_MAXchdir()版本沒有這個問題。
  • chdir()版本操縱pwd,如果可以避免,pwd通常被認爲是不好的做法:它不是線程安全的,並且最終用戶可能會認爲它是獨立的。例如,在命令行中給出的文件名以及程序的不同部分所使用的文件名可能與用戶認爲pwd是什麼有關,當您更改它時會中斷它。
  • 備份到較高目錄(chdir(".."))時,chdir()版本可能會失去控制,如果不採取特別措施並且在遍歷目錄結構時發生更改。然後在這種情況下,完整的路徑名版本可能會以不同的方式破壞...

現代POSIX系統上可用的openat()系列功能提供了兩全其美的功能。如果這些功能可用,openat()連同fdopendir(),fstatat()等等,使得目錄散步非常好。

+0

感謝您的洞察力和其他功能! – rsa