文章目录
 前期准备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);
}
 
运行结果 :
 
 
编者的这种方法仅仅是为了完成而完成 , 它的效率并不高 , 尤其是在判断文件种类的时候 .这代码仅供学习吧 , 如果有人有更好的方法 , 可以来交流.