实验三 目录树的遍历
一、目的
掌握与文件和目录树有关的系统调用和库函数。
二、要求
1、编写程序myfind
命令语法:myfind <pathname> [-comp <filename> | -name <str>…]
命令语义:
(1)myfind <pathname> 的功能:
除了具有与程序4-7相同的功能外,还要输出在<pathname>目录子树之下,文件长度不大于4096字节的常规文件,在所有允许访问的普通文件中所占的百分比。程序不允许打印出何路径名。
(2)myfind <pathname> -comp <filename>的功能:
<filename>是常规文件的路径名(非目录名,但是其路径可以包含目录)。命令仅仅输出在<pathname>目录子树之下,所有与<filename>文件内容一致的文件的绝对路径名。不允许输出任何其它的路径名,包括不可访问的路径名。
(3)myfind <pathname> -name <str>…的功能:
<str>…是一个以空格分隔的文件名序列(不带路径)。命令输出在<pathname>目录子树之下,所有与<str>…序列中文件名相同的文件的绝对路径名。不允许输出不可访问的或无关的路径名。 <pathname>和<filename>均既可以是绝对路径名,也可以是相对路径名。<pathname>既可以是目录,也可以是文件,此时,目录为当前工作目录。
2、注意尽可能地提高程序的效率。注意避免因打开太多文件而产生的错误。
3、遍历目录树时,访问结点(目录项)的具体操作应当由遍历函数dopath携带的函数指针参数决定。这样程序的结构清晰,可扩充性好。
三、设计和实现的主要原理、构思、算法
#include"apue.h"
#include<dirent.h>
#include<limits.h>
#include<sys/stat.h>
#include<string.h>
#include<malloc.h>
#include<fcntl.h>
char*path_alloc(int * size)//定义path_alloc()这个函数,path_alloc函数主要是为路径(完整路径)分配内存空间
{
char*p=NULL;
if(!size)return NULL;
p=malloc(256);
if(p)
*size=256;
else
*size=0;
returnp;
}
typedefint Myfunc(const char*,const struct stat *,int);//声明了一个新的函数类型Myfunc,这种函数类型带3个参数,返回值是int类型的
staticMyfunc myfunc1,myfunc2,myfunc3;//声明了三个对象
staticint myftw(char *,Myfunc *);//声明函数myftw的原形,其参数两个,其一为一指针,其二为一函数名
staticint dopath(Myfunc *);//dopath函数,主要是通过递归获取路径,并判断是目录还是文件,从而转向myfunc函数进一步判断并计数
staticlong nreg,ndir,nblk,nchr,nfifo,nslink,nsock,ntot,size,dsize,count;//全局变量,ntot是表示文件的总数
staticchar *dbuf,*filename;
int
main(intargc,char *argv[])//主函数
{
int ret,fd,i;
struct stat buf;
if(strcmp(argv[0],"./myfind")!=0)err_ret("Input error.\n");
if(argc==2)
{
ret=myftw(argv[1],myfunc1);
ntot=nreg+ndir+nblk+nchr+nfifo+nslink+nsock;
if(ntot==0)ntot=1;
printf("totalfiles=%7ld\n",ntot);
printf("regular files =%7ld, %5.2f%%\n",nreg,nreg*100.0/ntot);
printf("directories =%7ld, %5.2f%%\n",ndir,ndir*100.0/ntot);
printf("block special =%7ld, %5.2f%%\n",nblk,nblk*100.0/ntot);
printf("char special =%7ld, %5.2f%%\n",nchr,nchr*100.0/ntot);
printf("FIFOs =%7ld, %5.2f%%\n",nfifo,nfifo*100.0/ntot);
printf("symbolic links =%7ld, %5.2f%%\n",nslink,nslink*100.0/ntot);
printf("sockets =%7ld, %5.2f%%\n",nsock,nsock*100.0/ntot);
printf("filesize<4096 =%7ld, %5.2f %%\n",size,size*100.0/ntot);
}
else if(argc>=4)
{
if(argc==4&&strcmp(argv[2],"-comp")==0)
{
if((fd=open(argv[3],O_RDONLY))==-1)
err_ret("can'topen the file %s\n",argv[4]);
lstat(argv[3],&buf);// lstat函数通过文件名filename获取文件信息,并保存在buf所指的结构体stat中
dsize=buf.st_size;
dbuf=(char*)malloc(sizeof(char)*dsize);
read(fd,dbuf,dsize);
close(fd);
count=0;
ret=myftw(argv[1],myfunc2);
if(count==0)printf("can't find the file.\n");
}
if(strcmp(argv[2],"-name")==0)
{
for(i=4;i<=argc;i++)
{
count=0;
printf("thepaths of this file '%s':\n",argv[i-1]);
filename=argv[i-1];
ret=myftw(argv[1],myfunc3);
if(count==0)printf("Nosuch path.\n");
}
}
}
exit(ret);
}
#defineFTW_F 1
#defineFTW_D 2
#defineFTW_DNR 3
#defineFTW_NS 4
staticchar *fullpath;
staticint
myftw(char*pathname,Myfunc *func)
{
int len;
fullpath=path_alloc(&len);
strncpy(fullpath,pathname,len);
fullpath[len-1]=0;
return(dopath(func));
}
staticint
dopath(Myfunc*func)
{
struct stat statbuf;
struct dirent *dirp;
DIR *dp;
int ret;
char *ptr;
if(lstat(fullpath,&statbuf)<0)
return(func(fullpath,&statbuf,FTW_NS));
if(S_ISDIR(statbuf.st_mode)==0)
return(func(fullpath,&statbuf,FTW_F));
if((ret=func(fullpath,&statbuf,FTW_D))!=0)
return(ret);
ptr=fullpath+strlen(fullpath);
*ptr++='/';
*ptr=0;
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;
strcpy(ptr,dirp->d_name);
if((ret=dopath(func))!=0)
break;
}
ptr[-1]=0;//等同 *[ptr-1]=0;
if(closedir(dp)<0)
err_ret("cant close directory%s",fullpath);
return(ret);
}
staticint
myfunc1(constchar *pathname,const struct stat *statptr,int type)
{
switch(type){
case FTW_F:
if(statptr->st_size<4096)size++;
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:
err_dump("for S_IFDIRfor %s",pathname);
}
break;
case FTW_D:
ndir++;
break;
case FTW_DNR: break;
case FTW_NS: break;
}
return(0);
}
staticint
myfunc2(constchar *pathname,const struct stat *statptr,int type)
{
int fd,len;
char *buf,*fullpathname;
buf=(char *)malloc(sizeof(char)*dsize);
fullpathname=path_alloc(&len);
if(type==FTW_F)
{
if(dsize==statptr->st_size)
{
if((fd=open(pathname,O_RDONLY))==-1)
err_ret("cant'topen file\n");
read(fd,buf,dsize);
if(strcmp(dbuf,buf)==0)
{
getcwd(fullpathname,len);
count++;
if(*pathname=='/')printf("%s\n",pathname);
elseif(*pathname=='.')
{
strcat(fullpathname,pathname+2);
printf("%s\n",fullpathname);
}
else
{
strcat(fullpathname,"/");
strcat(fullpathname,pathname);
printf("%s\n",fullpathname);
}
}
close(fd);
}
}
return(0);
}
staticint
myfunc3(constchar *pathname,const struct stat *statptr,int type)
{
int len;
char *fullpathname;
const char *q;
fullpathname=path_alloc(&len);
if(type==FTW_F)
{
getcwd(fullpathname,len);
q=pathname+(strlen(pathname)-1);
while(*q!='/') q--;
if(strcmp(filename,++q)==0)
{
count++;
if(*pathname=='/')printf("%s\n",pathname);
else if(*pathname=='.')
{
strcat(fullpathname,pathname+2);
printf("%s\n",fullpathname);
}
else
{
strcat(fullpathname,"/");
strcat(fullpathname,pathname);
printf("%s\n",fullpathname);
}
}
}
return(0);
}
四、实验运行及结果
源程序名:myfind.c
可执行文件:myfind
编译方法:gcc myfind.c error2e.c –o myfind
结束方法:ctrl+c
运行过程:
1.编译
2.实现第一个功能:
其中在/home/gu12/gu123104/expmt/3文件目录中总共存在六个文件,其中一个目录文件(.开头的隐藏文件),另有一个小于4096字节的文件(error2e.c).
3.实现第二个功能:判定文件名相同且文件内容一样
当我们对其中/home/gu12/gu123104/expmt/3目录中的apue.h做一下修改,再去实现,则情况如下:
也就找不到内容一样的apue.h文件了,只有本身。
4.实现第三个功能:
1)通过绝对路径实现:
尽管我们刚把apue.h做过了修改,但还是把目录下所有的apue.h文件都输出了,因为他只需要找到相同的名字。
2)通过相对路径实现:
在相对路径下也能实现。
① 总结
a. length=lseek(fd2,0,SEEK_END);
用于确定文件的长度
b. 原型声明:externchar *strcpy(char *dest,const char *src);
头文件:string.h
功能:把从src地址开始且含有NULL结束符的字符串赋值到以dest开始的地址空间
说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
返回指向dest的指针。
c. 语法:struct dirent* readdir(DIR* dir_handle);
返回值: dirent的结构类型
函数种类: 文件存取
内容说明本函数用来读取目录。返回是dirent结构体指针,dirent结构体成员如下,
struct dirent
{
long d_ino; /* inode number 索引节点号 */
off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
unsigned short d_reclen; /* length of this d_name 文件名长 */
unsigned char d_type; /* the type of d_name 文件类型 */
char d_name[NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}