使用chdir()而不是绝对路径进行目录遍历
在“Unix环境中的高级编程”一书的第4章中,介绍了文件和目录,其中有一个代码示例,旨在类似于使用chdir()而不是绝对路径进行目录遍历,c,unix,directory,chdir,directory-walk,C,Unix,Directory,Chdir,Directory Walk,在“Unix环境中的高级编程”一书的第4章中,介绍了文件和目录,其中有一个代码示例,旨在类似于ftw命令并遍历文件层次结构。它使用指向绝对文件路径的指针,以及带有回调的递归函数来遍历目录,在这个过程中使用对opendir()和readdir()的调用 有一个练习要求读者使用chdir()和文件名,而不是使用绝对路径来完成相同的任务并比较两个程序的时间。我使用chdir()编写了一个程序,没有注意到时间上的差异。这是预期的吗?我本以为对chdir()的额外调用会增加一些开销。这可能是一个相对琐碎的
ftw
命令并遍历文件层次结构。它使用指向绝对文件路径的指针,以及带有回调的递归函数来遍历目录,在这个过程中使用对opendir()
和readdir()
的调用
有一个练习要求读者使用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/*我们返回func()返回的任何内容*/
dopath(Myfunc*func)
{
结构stat statbuf;
结构方向*dirp;
DIR*dp;
int ret;
char*ptr;
if(lstat(完整路径,&statbuf)<0)/*统计错误*/
返回(func(fullpath,和statbuf,FTW_NS));
如果(S_ISDIR(statbuf.st_mode)==0)/*不是目录*/
返回(func(fullpath和statbuf,FTW_F));
/*
*这是一个目录。首先调用该目录的func(),
*然后处理目录中的每个文件名。
*/
如果((ret=func(完整路径和固定路径,FTW_D))!=0)
返回(ret);
ptr=完整路径+strlen(完整路径);/*指向完整路径的末尾*/
*ptr++='/';
*ptr=0;
if((dp=opendir(fullpath))==NULL)/*无法读取目录*/
返回(func(完整路径和固定窗口,FTW_DNR));
而((dirp=readdir(dp))!=NULL){
如果(strcmp(dirp->d_name,”)==0||
strcmp(dirp->d_name,“..”==0)
继续;/*忽略点和点*/
strcpy(ptr,dirp->d_name);/*在斜杠后追加名称*/
如果((ret=dopath(func))!=0)/*递归*/
休息;/*该走了*/
}
ptr[-1]=0;/*从斜杠开始删除所有内容*/
if(闭合方向(dp)<0)
err_ret(“无法关闭目录%s”,完整路径);
返回(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);
}
static int/*我们返回func()返回的任何内容*/
dopath(Myfunc*func,char*path)
{
结构stat statbuf;
结构方向*dirp;
DIR*dp;
int ret;
if(lstat(path,&statbuf)<0)/*统计错误*/
返回值(func(路径和statbuf,FTW_NS));
如果(S_ISDIR(statbuf.st_mode)==0)/*不是目录*/
返回(func(路径和statbuf,FTW_F));
/*
*这是一个目录。首先调用该目录的func(),
*然后处理目录中的每个文件名。
*/
如果((ret=func(路径和状态函数,FTW_D))!=0)
返回(ret);
if(chdir(路径)<0)
返回(func(路径和statbuf,FTW_DNR));
if((dp=opendir(“.”)==NULL)/*无法读取目录*/
返回(func(路径和statbuf,FTW_DNR));
而((dirp=readdir(dp))!=NULL){
如果(strcmp(dirp->d_name,”)==0||
strcmp(dirp->d_name,“..”==0)
继续;/*忽略点和点*/
如果((ret=dopath(func,dirp->d_name))!=0)/*递归*/
休息;/*该走了*/
}
if(chdir(“..”)<0)
err_ret(“无法进入目录”);
if(闭合方向(dp)<0)
err_ret(“无法关闭目录%s”,完整路径);
返回(ret);
}
我认为绝对路径版本和chdir()
版本之间的时间性能差异不大。相反,两种版本的优缺点如下:
- 完整路径名版本可能无法遍历非常深的目录结构,因为完整路径名的长度最终超过了
。PATH\u MAX
版本没有此问题chdir()
版本操纵pwd,如果可以避免的话,这通常被认为是不好的做法:它不是线程安全的,最终用户可能希望它被单独处理。例如,命令行中给出的文件名以及程序不同部分使用的文件名可能与用户认为的pwd有关,当您更改pwd时,pwd会中断chdir()
- 如果没有特别注意,并且在遍历目录结构时目录结构发生更改,则备份到更高目录(
)时,chdir(“…”
chdir()版本可能会失控。在这种情况下,完整的路径名版本可能会以不同的方式崩溃
现代POSIX系统上可用的
openat()
函数系列提供了这两个方面的最佳选择。如果这些函数可用,openat()
以及fdopendir()
、fstatat()
等。。。这是一个非常好的目录遍历实现。我认为绝对路径版本和chdir()
版本之间不应该有很大的时间性能差异。相反,两种版本的优缺点如下:
- 完整路径名版本可能无法遍历非常深的目录结构,因为完整路径名的长度最终超过了
。PATH\u MAX
版本没有此问题chdir()
版本操纵pwd,如果可以避免的话,这通常被认为是不好的做法:它不是线程安全的,最终用户可能希望它被单独处理。例如,在命令行中给出的文件名,在chdir()