文章目录
前期准备DIR结构体dirent结构体stat结构体st_mode结构体
ls的实现大致思路以下是源代码:运行结果 :
ls -l 的实现大致思路:1. total值的计算2. 文件时间的显示tm结构体
以下是源代码:运行结果 :
前期准备
DIR结构体
首先开始先来看看DIR这个结构体 , 以下为DIR结构体的定义 :
struct __dirstream
{
void *__fd
;
char *__data
;
int __entry_data
;
char *__ptr
;
int __entry_ptr
;
size_t __allocation
;
size_t __size
;
__libc_lock_define
(, __lock
)
};
typedef struct __dirstream DIR
;
DIR结构体类似于FILE,是一个内部结构,以下几个函数用这个内部结构保存当前正在被读取的目录的有关信息(摘自《UNIX环境高级编程(第二版)》). 函数 DIR *opendir(const char *pathname),即打开文件目录,返回的就是指向DIR结构体的指针,而该指针由以下几个函数使用:
struct dirent
*readdir(DIR
*dp
);
void rewinddir(DIR
*dp
);
int closedir(DIR
*dp
);
long telldir(DIR
*dp
);
void seekdir(DIR
*dp
,long loc
);
关于DIR的结构 , 我们了解这么对就可以了.
dirent结构体
接着在来看看dirent结构体 , 首先我们要弄清楚目录文件(directory file)的概念:这种文件包含了其他文件的名字以及指向与这些文件有关的信息的指针(摘自《UNIX环境高级编程(第二版)》) .从定义能够看出,dirent不仅仅指向目录,还指向目录中的具体文件,readdir函数同样也读取目录下的文件,这就是证据。以下为dirent结构体的定义:
struct dirent
{
long d_ino
;
off_t d_off
;
unsigned short d_reclen
;
unsigned char d_type
;
char d_name
[NAME_MAX
+1];
}
从上述定义也能够看出来,dirent结构体存储的关于文件的信息很少,所以dirent同样也是起着一个索引的作用,如果想获得类似ls -l那种效果的文件信息,必须要靠stat函数了 .
stat结构体
通过readdir函数读取到的文件名存储在结构体dirent的d_name成员中,而函数
int stat(const char *file_name, struct stat *buf);
的作用就是获取文件名为d_name的文件的详细信息,存储在stat结构体中。以下为stat结构体的定义:
struct stat
{
mode_t st_mode
;
ino_t st_ino
;
dev_t st_dev
;
dev_t st_rdev
;
nlink_t st_nlink
;
uid_t st_uid
;
gid_t st_gid
;
off_t st_size
;
time_t st_atime
;
time_t st_mtime
;
time_t st_ctime
;
blksize_t st_blksize
;
blkcnt_t st_blocks
;
};
这样前期的准备工作就做的差不多了 , 让我们来看看ls怎么实现 .
st_mode结构体
st_mode这个变量用来判断文件类型 st_mode是用特征位来表示文件类型的,特征位的定义如下:
S_IFMT
0170000 文件类型的位遮罩
S_IFSOCK
0140000 socket
S_IFLNK
0120000 符号链接
(symbolic link
)
S_IFREG
0100000 一般文件
S_IFBLK
0060000 区块装置
(block device
)
S_IFDIR
0040000 目录
S_IFCHR
0020000 字符装置
(character device
)
S_IFIFO
0010000 先进先出
(fifo
)
S_ISUID
0004000 文件的
(set user
-id on execution
)位
S_ISGID
0002000 文件的
(set group
-id on execution
)位
S_ISVTX
0001000 文件的sticky位
S_IRWXU
00700 文件所有者的遮罩值
(即所有权限值
)
S_IRUSR
00400 文件所有者具可读取权限
S_IWUSR
00200 文件所有者具可写入权限
S_IXUSR
00100 文件所有者具可执行权限
S_IRWXG
00070 用户组的遮罩值
(即所有权限值
)
S_IRGRP
00040 用户组具可读取权限
S_IWGRP
00020 用户组具可写入权限
S_IXGRP
00010 用户组具可执行权限
S_IRWXO
00007 其他用户的遮罩值
(即所有权限值
)
S_IROTH
00004 其他用户具可读取权限
S_IWOTH
00002 其他用户具可写入权限
S_IXOTH
00001 其他用户具可执行权限
摘自《Linux C 函数库参考手册》
ls的实现
大致思路
大致的思路是(因为时间过的有点长 , 当时是有些具体的想法都忘记了 , 将就着看吧) : 编者要完成的是一个经过排序的ls . 首先根据DIR这个结构体 , 打开目录 , 再根据dirent这个结构体 , 读出目录中的文件名 , 并把它保存在一个二维数组中(编者这里将数组设置成了100*100 , 足够保存) , 然后对于目录文件和可执行文件进行特殊标记 (这里就是在最后 , 标记一个数字 , 因为文件名肯定取不到) , 因为在Linux中不同的文件的颜色是不同的, 然后使用快排对文件名进行排序 , 然后在输出的时候对后最一个位置进行甄别 ,如果是可执行文件或者目录的话, 使其输出相应的颜色. 对于文件名的快排 , 附上链接同大家学习. https://blog.csdn.net/f_zyj/article/details/51484751
以下是源代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include<dirent.h>
#include<sys/stat.h>
#define SIZE_MAX 100
int cmp(const void *a
,const void *b
)
{
return (strcmp((char*)a
,(char*)b
));
}
int main()
{
char path
[256] = {0};
getcwd(path
,256);
DIR
*pdir
= opendir(path
);
if(pdir
== NULL)
{
exit(1);
}
struct dirent
*p
= NULL;
struct stat st
;
int count
= 0;
char s
[SIZE_MAX
][SIZE_MAX
];
while((p
= readdir(pdir
)) != NULL)
{
if(strncmp(p
->d_name
,".",1) == 0)
{
continue;
}
stat(p
->d_name
,&st
);
if(S_ISDIR(st
.st_mode
))
{
s
[count
][SIZE_MAX
-1] = '9';
}
else
{
if(st
.st_mode
&(S_IXUSR
| S_IXGRP
| S_IXOTH
))
{
s
[count
][SIZE_MAX
-1] = '8';
}
}
strcpy(s
[count
++],p
->d_name
);
}
qsort(s
,count
,sizeof(s
[0]),cmp
);
int i
= 0;
for(i
= 0;i
< count
;i
++)
{
if(s
[i
][SIZE_MAX
-1] == '9')
{
printf("\033[1;34m%s \033[0m",s
[i
]);
}
else
{
if(s
[i
][SIZE_MAX
-1] == '8')
{
printf("\033[1;32m%s \033[0m",s
[i
]);
}
else
{
printf("%s ",s
[i
]);
}
}
}
printf("\n");
closedir(pdir
);
exit(0);
}
运行结果 :
ls -l 的实现
大致思路:
获取当前的工作目录 , 讲读取到的文件都写入 自己创建的二维数组当中 , 然后进行快排 , 之后再与DIR结构结构体中读取到的目录进行比对 , 根据 st_mode 来对文件进行判断 , 然后在进行输出(跟ls的实现基本相同). 那么来说说在编写的时候遇到的问题 :
1.total值的计算2.文件时间的显示
1. total值的计算
由于total的单位是k , 4096是块的默认大小 . 所以需要做的是 , 将文件所有的文件大小加起来 , 然后计算出所占的块数(不满一块的按照一块来计算) , 然后根据 total = 块数 * 4 , 就能得出total的值. 注意 : ls -l 是不计算符号链接的 .
2. 文件时间的显示
在ls -l的命令中 , 会显示文件的创建时间 . 所以要使用 localtime() 用来获取系统时间(精度为秒) , 这样的话就能正确输出时间.
tm结构体
#ifndef _TM_DEFINED
struct tm
{
int tm_sec
;
int tm_min
;
int tm_hour
;
int tm_mday
;
int tm_mon
;
int tm_year
;
int tm_wday
;
int tm_yday
;
int tm_isdst
;
};
#define _TM_DEFINED
#endif
以下是源代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include<dirent.h>
#include<sys/stat.h>
#include<pwd.h>
#include<grp.h>
#include<time.h>
#define SIZE_MAX 100
int cmp(const void *a
,const void *b
)
{
return (strcmp((char*)a
,(char*)b
));
}
int main()
{
char path
[256] = {0};
getcwd(path
,256);
DIR
*pdir
= opendir(path
);
if(pdir
== NULL)
{
exit(1);
}
struct dirent
*p
= NULL;
struct stat st
;
int total
= 0;
long block
=0;
char s
[SIZE_MAX
][SIZE_MAX
] = {0};
int count
= 0;
while((p
= readdir(pdir
)) != NULL)
{
stat(p
->d_name
,&st
);
if(strncmp(p
->d_name
,".",1) == 0)
{
continue;
}
if(st
.st_size
<= 4096)
{
block
++;
}
else
{
block
+= (st
.st_size
/4096) +1;
}
strcpy(s
[count
++],p
->d_name
);
}
qsort(s
,count
,sizeof(s
[0]),cmp
);
closedir(pdir
);
total
= block
*4;
printf("total %d\n",total
);
int i
= 0;
for(i
= 0;i
< count
;i
++)
{
pdir
= opendir(path
);
while((p
= readdir(pdir
)) != NULL)
{
if(strncmp(p
->d_name
,".",1) == 0)
{
continue;
}
if(strcmp(p
->d_name
,s
[i
]) != 0)
{
continue;
}
stat(p
->d_name
,&st
);
if(S_ISDIR(st
.st_mode
))
{
printf("d");
}
if((st
.st_mode
& S_IFREG
) == S_IFREG
)
{
printf("-");
}
if((st
.st_mode
& S_IFLNK
) == S_IFLNK
)
{
printf("l");
}
if((st
.st_mode
& S_IFCHR
) == S_IFCHR
)
{
printf("c");
}
if((st
.st_mode
& S_IFBLK
) == S_IFBLK
)
{
printf("b");
}
if((st
.st_mode
& S_IFIFO
) == S_IFIFO
)
{
printf("p");
}
if((st
.st_mode
& S_IFSOCK
) == S_IFSOCK
)
{
printf("s");
}
if((st
.st_mode
& S_IRUSR
) == S_IRUSR
)
{
printf("r");
}
else
{
printf("-");
}
if((st
.st_mode
& S_IWUSR
) == S_IWUSR
)
{
printf("w");
}
else
{
printf("-");
}
if((st
.st_mode
& S_IXUSR
) == S_IXUSR
)
{
printf("x");
}
else
{
printf("-");
}
if((st
.st_mode
& S_IRGRP
) == S_IRGRP
)
{
printf("r");
}
else
{
printf("-");
}
if((st
.st_mode
& S_IWGRP
) == S_IWGRP
)
{
printf("w");
}
else
{
printf("-");
}
if((st
.st_mode
& S_IXGRP
) == S_IXGRP
)
{
printf("x");
}
else
{
printf("-");
}
if((st
.st_mode
& S_IROTH
) == S_IROTH
)
{
printf("r");
}
else
{
printf("-");
}
if((st
.st_mode
& S_IWOTH
) == S_IWOTH
)
{
printf("w");
}
else
{
printf("-");
}
if((st
.st_mode
& S_IXOTH
) == S_IXOTH
)
{
printf("x");
}
else
{
printf("-");
}
printf(". ");
printf("%d ",st
.st_nlink
);
struct passwd
*pwd
;
pwd
= getpwuid(st
.st_uid
);
printf("%-4s ",pwd
->pw_name
);
struct group
*grp
;
grp
= getgrgid(st
.st_gid
);
printf("%-4s ",grp
->gr_name
);
printf("] ",st
.st_size
);
struct tm
*t
= localtime(&st
.st_mtime
);
char *wday
[12] = {"Jan","Feb","Mar","Apr","May","June","July","Aug","Sept","Oct","Nov","Dec"};
printf("%s %d ",wday
[t
->tm_mon
],t
->tm_mday
);
if(t
->tm_hour
< 10)
{
printf("0%d:",t
->tm_hour
);
}
else
{
printf("%d:",t
->tm_hour
);
}
if(t
->tm_min
< 10)
{
printf("0%d ",t
->tm_min
);
}
else
{
printf("%d ",t
->tm_min
);
}
if(S_ISDIR(st
.st_mode
))
{
printf("\033[1;34m%s \n\033[0m",p
->d_name
);
}
else
{
if(st
.st_mode
&(S_IXUSR
| S_IXGRP
| S_IXOTH
))
{
printf("\033[1;32m%s \n\033[0m",p
->d_name
);
}
else
{
printf("%s \n",p
->d_name
);
}
}
}
closedir(pdir
);
}
exit(0);
}
运行结果 :
编者的这种方法仅仅是为了完成而完成 , 它的效率并不高 , 尤其是在判断文件种类的时候 .这代码仅供学习吧 , 如果有人有更好的方法 , 可以来交流.