Linux下ls命令的实现解析.docx
《Linux下ls命令的实现解析.docx》由会员分享,可在线阅读,更多相关《Linux下ls命令的实现解析.docx(17页珍藏版)》请在冰豆网上搜索。
![Linux下ls命令的实现解析.docx](https://file1.bdocx.com/fileroot1/2022-11/21/92ac0452-6912-4646-a15a-ebe57512c349/92ac0452-6912-4646-a15a-ebe57512c3491.gif)
Linux下ls命令的实现解析
《Linux环境程序设计》大作业报告
题目:
Linux下ls命令的实现
学院物联网工程学院
专业计算机科学与技术
班级计科1105班
学号03041105
学生姓名
二〇一四年十二月
目录
一、设计思想2
1.1实验要求2
1.2设计思路2
二、数据定义、系统(函数)调用、处理流程2
2.1数据定义2
2.1.1DIR结构体2
2.1.2dirent结构体3
2.1.3stat结构体3
2.2系统调用4
2.2.1opendir函数4
2.2.2readdir函数4
2.2.3closedir函数5
2.3处理流程5
三、详细设计(含源程序)6
四、运行结果与分析11
五、设计体会14
六、参考文献14
一、设计思想
1.1实验要求
使用opendir、readdir、closedir等函数来操作目录,利用stat函数来获取文件信息。
编写一个功能完整的实现Linux下ls命令的程序,该程序实现了-l、-i、-t这几个选项的功能。
其中,-l、-i、-t选项说明:
-l:
usealonglistingformat.
-i:
printtheindexnumberofeachfile.
-t:
sortbymodificationtime,newestfirst.
1.2设计思路
本实验是实现Linux下的ls功能。
其设计思路如下:
目的是获取某目录下文件的详细信息。
(1)首先使用opendir()函数打开目录,返回指向该目录的DIR结构体。
(2)接着,调用readdir()函数读取这个目录下所有文件,其中应该包括目录本身,返回指向该目录下所有文件的dirent结构体。
(3)最后,遍历dirent结构体,调用stat来获取每个文件的详细信息并存储在stat结构体中。
如果这个参数是一个文件名,我们输出这个文件的大小和最后修改的时间,如果是一个目录我们输出这个目录下所有文件的大小和修改时间。
使用一个数组flags[4]来标记输入的命令是-l、-i、-t还是空(没有输入命令)。
当选择-l功能时,是以长列表方式显示,即显示详细信息;当选择-i功能时,是显示每一个文件在系统里的文件号;当选择-t功能时,是按修改时间来排序,以最新的修改时间来输出。
二、数据定义、系统(函数)调用、处理流程
2.1数据定义
2.1.1DIR结构体
该结构体包含在头文件#include中,其定义如下:
struct__dirstream
{
void *__fd;
char *__data;
int __entry_data;
char *__ptr;
int __entry_ptr;
size_t __allocation;
size_t __size;
__libc_lock_define (, __lock)
};
typedefstruct__dirstreamDIR;
DIR结构体类似于FILE,是一个内部结构,以下几个函数用这个内部结构保存当前正在被读取的目录的有关信息。
函数DIR*opendir(constchar*pathname),即打开文件目录,返回的就是指向DIR结构体的指针,而该指针由以下几个函数使用:
struct dirent *readdir(DIR *dp);
void rewinddir(DIR *dp);
int closedir(DIR *dp);
long telldir(DIR *dp);
void seekdir(DIR *dp, longloc);
2.1.2dirent结构体
对于dirent结构体,首先我们要弄清楚目录文件(directoryfile)的概念。
这种文件包含了其他文件的名字以及指向与这些文件有关的信息的指针。
从定义能够看出,dirent不仅仅指向目录,还指向目录中的具体文件,readdir函数同样也读取目录下的文件。
以下为dirent结构体的定义:
structdirent
{
long d_ino; /* inodenumber索引节点号 */
off_t d_off; /* offset to thisdirent在目录文件中的偏移 */
unsignedshort d_reclen; /* lengthofthisd_name 文件名长 */
unsignedchar d_type; /* thetypeofd_name 文件类型 */
char d_name[NAME_MAX+1]; /* filename (null-terminated) 文件名,最长255字符 */
}
从上述定义也能够看出来,dirent结构体存储的关于文件的信息很少,所以dirent同样也是起着一个索引的作用。
2.1.3stat结构体
如果想获得类似ls-l那种效果的文件信息,必须要靠stat函数了。
通过readdir函数读取到的文件名存储在结构体dirent的d_name成员中,而函数
intstat(constchar*file_name,structstat*buf);的作用就是获取文件名为d_name的文件的详细信息,存储在stat结构体中。
以下为stat结构体的定义:
struct stat{
mode_tst_mode; //文件访问权限
ino_t st_ino; //索引节点号
dev_t st_dev; //文件使用的设备号
dev_t st_rdev; //设备文件的设备号
nlink_tst_nlink; //文件的硬连接数
uid_t st_uid; //所有者用户识别号
gid_t st_gid; //组识别号
off_t st_size; //以字节为单位的文件容量
time_tst_atime; //最后一次访问该文件的时间
time_tst_mtime; //最后一次修改该文件的时间
time_tst_ctime; //最后一次改变该文件状态的时间
blksize_tst_blksize; //包含该文件的磁盘块的大小
blkcnt_tst_blocks; //该文件所占的磁盘块
};
2.2系统调用
2.2.1opendir函数
该函数的功能是打开目录。
相关函数
open,readdir,closedir,rewinddir,seekdir,telldir,scandir
表头文件
#include
#include
定义函数
DIR*opendir(constchar*name);
函数说明
opendir()用来打开参数name指定的目录,并返回DIR*形态的目录流,和open()类似,接下来对目录的读取和搜索都要使用此返回值。
返回值
如果成功则返回DIR*型态的目录流,如果打开失败则返回NULL。
2.2.2readdir函数
该函数的功能是读取目录。
相关函数
open,opendir,closedir,rewinddir,seekdir,telldir,scandir
表头文件
#include
#include
定义函数
structdirent*readdir(DIR*dir);
函数说明
readdir()返回参数dir目录流的下个目录进入点。
返回值
成功则返回下个目录进入点。
有错误发生或读取到目录文件尾则返回NULL。
附加说明
EBADF参数dir为无效的目录流。
2.2.3closedir函数
该函数的功能是关闭目录。
相关函数
opendir
表头文件
#include
#include
定义函数
intclosedir(DIR*dir);
函数说明
closedir()关闭参数dir所指的目录流。
返回值
如果关闭成功则返回0,如果失败返回-1,错误原因存于errno中。
2.3处理流程
否
是
三、详细设计(含源程序)
本实验是实现Linux下的ls功能。
首先设置标志,判断输入的命令。
如果是普通文件,则递归输出文件的相关信息,如文件的大小和最后修改时间。
如果是目录,则输出该目录下的所有文件和非正常文件的大小以及修改时间。
普通文件使用S_ISREG()函数,而目录文件使用S_ISDIR()函数。
源代码如下:
/*
*************************
Linux下的ls功能实现
计科1105班颜潇雨
*************************
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
staticboolflags[4];//标志,选项设定全局变量,用来确定是-l,-t还是-i
intmyls(char*s,boolflag[]);
voidlprint(char*fname);
/*main函数入口*/
intmain(intargc,char**argv)
{
charc;
inti;
while((c=getopt(argc,argv,"alidtFR"))!
=-1)//获取命令行输入的选项参数
{
switch(c)
{
case'l':
flags[0]=1;break;
case'i':
flags[1]=1;break;
case't':
flags[2]=1;break;
default:
return-1;
}
}
if(argc==optind)//命令行没有输入目录参数,设定函数目录参数为当前目录
{
myls(".",flags);
}
else
{
for(i=optind;imyls(argv[i],flags);
}
return0;
}
/*myls函数,调用lprint函数进行-l格式输出*/
intmyls(char*s,boolflag[])
{
structstatbuf,statbuf,tmps;
structstatsort[256];
char*t;
charfname[256],tmpn[256],sfname[256];
charsname[256][256];
DIR*dir;
structdirent*pd;
intnum,i,j;
if(lstat(s,&buf)<0)return-1;//读取文件状态信息
if(S_ISDIR(buf.st_mode))//处理目录文件
{
dir=opendir(s);//打开目录
/*处理-t*/
/*对目录下所有文件进行排序,存放在缓存中进行按序输出*/
if(flag[2])
{
num=0;
while((pd=readdir(dir))!
=NULL)//遍历目录下所有文件
{
if((pd->d_name[0]=='.')||(pd->d_name[0]!
='.'))
{
memset(fname,0,256);
strcpy(fname,s);
strcat(fname,"/");
strcat(fname,pd->d_name);
lstat(fname,&sort[num]);//读取文件信息到缓存数组sort[]中
strcpy(sname[num],pd->d_name);//保存文件名到缓存数组sname[]中
num++;
}
}
/*将sort[]存储的文件信息进行排序*/
for(i=0;i{
for(j=i+1;jif(sort[i].st_mtime{
tmps=sort[i];sort[i]=sort[j];sort[j]=tmps;//交换sort[i]与sort[j]
/*交换sname[i]与sname[j]*/
strcpy(tmpn,sname[i]);
strcpy(sname[i],sname[j]);
strcpy(sname[j],tmpn);
}
}
/*将排序后的文件信息输出*/
for(i=0;i{
if(flag[1])printf("%ld\t",sort[i].st_ino);//-i
memset(sfname,0,256);
strcpy(sfname,s);
strcat(sfname,"/");
strcat(sfname,sname[i]);
if(flag[0])lprint(sfname);//-l
printf("%s",sname[i]);
if(flag[0])printf("\n");
elseprintf("\t");
}
printf("\n");
return0;
}
/*其他情况*/
else
{
while((pd=readdir(dir))!
=NULL)//遍历目录下所有文件
if((pd->d_name[0]=='.')||(pd->d_name[0]!
='.'))//-a
{
memset(fname,0,256);
strcpy(fname,s);
strcat(fname,"/");
strcat(fname,pd->d_name);
lstat(fname,&statbuf);
if(flag[1])printf("%ld\t",statbuf.st_ino);//-i
if(flag[0])lprint(fname);//-l
printf("%s",pd->d_name);
if(flag[0])printf("\n");
elseprintf("\t");
}
printf("\n");
return0;
}
}
else
{
if(flag[1])printf("%ld\t",buf.st_ino);//-i
if(flag[0])lprint(s);//-l
printf("%s",s);
if(flag[0])printf("\n");
elseprintf("\t");
}
printf("\n");
}
/*lprint函数处理-l格式输出*/
voidlprint(char*fname)
{
structstatbuf;
intn;
charlink[256];
structpasswd*pw;
structgroup*gr;
structtm*t;
lstat(fname,&buf);
switch(buf.st_mode&S_IFMT)//获取并转换后打印文件类型
{
caseS_IFREG:
printf("-");
break;
caseS_IFDIR:
printf("d");
break;
caseS_IFCHR:
printf("c");
break;
caseS_IFBLK:
printf("b");
break;
caseS_IFIFO:
printf("p");
break;
caseS_IFLNK:
printf("l");
break;
caseS_IFSOCK:
printf("s");
break;
}
for(n=8;n>=0;n--)//获取并转换后打印文件的读写属性
{
if(buf.st_mode&(1<{
switch(n%3)
{
case2:
printf("r");
break;
case1:
printf("w");
break;
case0:
printf("x");
break;
default:
break;
}
}
else
{
printf("-");
}
}
printf("%d",buf.st_nlink);//硬链接数
pw=getpwuid(buf.st_uid);//所属用户名
printf("%s",pw->pw_name);
gr=getgrgid(buf.st_gid);//所属组名
printf("%s",gr->gr_name);
printf("%ld",buf.st_size);//字节数
t=localtime(&buf.st_mtime);//最后修改时间
printf("%d-%d-%d%d:
%d"
t->tm_year+1900
t->tm_mon+1
t->tm_mday
t->tm_hour
t->tm_min);
if(S_ISLNK(buf.st_mode))//判断是否为链接
{
printf("->");
readlink(fname,link,100);
printf("%s",link);
}
printf("\t");
}
4、运行结果与分析
1.当不输入命令时,即为空命令。
运行结果如下:
2.-l命令运行结果如下:
3.-i命令运行结果如下:
4.-t命令运行结果如下:
5.-l-i组合命令运行结果如下:
6.-l-t组合命令运行结果如下:
7.-l-i-t组合命令运行结果如下:
此次实验的运行结果不仅包含单个的-l、-i、-t命令,而且还有两两之间的组合,甚至可以是三者的结合-l-i和-t一起输出。
五、设计体会
本次实验是实现Linux下的ls功能。
在实验过程中,首先初步熟悉了Linux系统,掌握了它的一些编程方法。
其次对Linux的一些系统函数以及结构体也有了一定的了解。
另外,通过这次实验,不仅使我对ls命令有了更深一层的认识,还学会了系统提供的命令实现的途径和方法。
从以前仅仅是单纯地使用系统命令到现在可以自己通过编程来实现一些命令,能够感觉到些许的成就感。
但同时,也发现了使用系统提供的命令和调用自己编写的命令感觉是相差很大的。
在这编程和使用的过程中,尽管出现了很多困难,软件不熟悉,代码不断报错出现bug,甚至是开发环境不知道如何配置,但是通过同学的帮助都顺利解决了这些问题。
所以这也表明了我们还需要不断的学习和进步,这样才能进一步完善我们的代码,努力做得更好。
六、参考文献
[1]W.Richard Stevens.Stephen A.Rago.UNIX环境高级编程(第二版)[M].人民邮电出版社.2006.05.
[2]MichaelKerrisk.Linux/UNIX系统编程手册.人民邮电出版社.2014.01.
[3]刘忆智.Linux从入门到精通.清华大学出版社.2010.01.
[4]Neil Matthew,Richard Stones.Linux程序设计.人民邮电出版社.2010.06.