0 个人信息
- 张樱姿
- 201821121038
- 计算1812
1 实验目的
- 通过编程进一步了解文件系统。
2 实验内容
- 在服务器上用Vim编写一个程序:实现Linux系统命令
ls -lai
的功能 - 给出运行结果截图,对于每一列是如何获取的,结合源代码做解释
3 实验报告
3.1 ls -lai简介
ls -l #以长格式显示目录下的内容列表。输出的信息从左到右依次包括文件名,文件类型、权限模式、硬连接数、所有者、组、文件大小和文件的最后修改时间等 ls -a #显示所有档案及目录(ls内定将档案名或目录名称为“.”的视为影藏,不会列出) ls -i #显示文件索引节点号(inode number),一个索引节点代表一个文件
3.2 实现过程
3.2.1 获取文件信息的函数及文件信息结构体
stat函数:
#include<sys/stat.h> int stat(const char * path,struct stat * buf); /*将path参数(文件或目录)的文件信息写到buf中,buf为传出参数*/
stat结构体:
struct stat{ dev_t st_dev; //设备id号(无需用到) ino_t st_ino; //索引节点号 mode_t st_mode; //权限与文件类型 nlink_t st_nlink; //硬链接数 uid_t st_uid; //用户id ggid_t st_gid; //所在组id dev_t st_rdev; //设备id,对于特殊文件才有(无需用到) off_t st_size; //大小,较为常用 blksize_t st_blocks; //文件系统I/O的块大小(无需用到) blkcnt_t st_blksize; //分配的512B(扇区)块数(无需用到) time_t st_atime; //最后的访问时间(无需用到) time_t st_mtime; //最后的修改时间,较为常用 time_t st_ctime; //最后的状态改变时间(无需用到) }
因此,需对上述相应字段格式化处理。
3.2.2 mode权限与类型判断
判断文件类型的宏函数:
S_ISREG(m) is it a regular file? //判断是否是普通文件 S_ISDIR(m) directory? //判断是否是目录 S_ISCHR(m) character device? //判断是否是字符设备 S_ISBLK(m) block device? //判断是否是块设备 S_ISFIFO(m) FIFO (named pipe)? //判断是否是管道文件 S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)//判断使是否是符号链接(软连接) S_ISSOCK(m) socket? (Not in POSIX.1-1996.) //判断是否是SOCKET文件
文件权限宏变量:
S_IRUSR 00400 //用户有读权限 S_IWUSR 00200 //用户有写权限 S_IXUSR 00100 //用户有执行权限 S_IRGRP 00040 //组有读权限 S_IWGRP 00020 //组有写权限 S_IXGRP 00010 //组有执行权限 S_IROTH 00004 //其他人有读权限 S_IWOTH 00002 //其他人有写权限 S_IXOTH 00001 //其他人有可执行权限
3.2.3 目录操作函数及目录信息结构体
opendir函数及readdir函数:
DIR * opendir(const char * name); //打开一个目录 struct dirent * readdir(DIR *); //读目录,依次返回目录的子项
dirent结构体:
struct dirent{ ino_t d_ino; //子项的i节点 off_t d_off; //节点的偏移量 unsigned short d_reclen;//长度 unsigned char d_type; //子项类型(常用) char d_name[256]; //子文件名(常用) };
3.2.4 表示时间的方式
①秒差形式,1970年1月1日0时0分0秒的秒数差,得到的类型为time_t;
②结构形式,tm结构体:
struct tm{ int tm_sec; //Second [0,60].包含闰秒 int tm_min; //Minutes [0,59]. int tm_hour; //Hour [0,23]. int tm_mday; //Day of month [1,31]. int tm_mon; //Month of year [0,11].(January = 0) int tm_year; //Year Since 1900. int tm_wday; //Day of week [0,6] (Sunday = 0). int tm_yday; //Day of year [0,365].包含闰年 int tm_isdat;//Daylight Savings flag }
计算机大多数情况使用time_t,因为效率高。但是显示时为tm结构形式。localtime()函数可以实现: time_t 到 tm 的转换。time_t的指针做参数,返回值tm的指针。
3.3 源代码
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<time.h> 5 #include<unistd.h> 6 #include<sys/types.h> 7 #include<dirent.h> 8 #include<grp.h> 9 #include<pwd.h> 10 #include<errno.h> 11 #include<sys/stat.h> 12 #include<limits.h> 13 #include<assert.h> 14 15 int flag = 0; 16 //用于分析参数 17 void AnalPara(int argc,char *argv[],char *path) 18 { 19 int i = 0; 20 for(i = 1;i < argc; ++i) 21 { //如果argv[i]中第一个字符是'-',则判断是l a i中的哪些 22 if(strncmp(argv[i],"-",1)==0) 23 { 24 //参数为a时,把1赋值给flag,flag所在内存中第一个位置为1 25 if(strstr(argv[i],"a") != NULL) 26 { 27 flag |= 1 << 0; ////-a参数显示该隐藏文件 28 } 29 //参数为l时,第二个位置为1 30 if(strstr(argv[i],"l") != NULL) 31 { 32 flag |= 1 << 1; //-l参数显示该文件的详细信息 33 } 34 //参数为i时,第三个位置为1 35 if(strstr(argv[i],"i") != NULL) 36 { 37 flag |= 1 << 2; //-i参数显示该文件的inode number 38 } 39 //位运算可使用一个变量同时标记多个参数是否传递 40 } 41 //如果argv[i]中第一个字符不是'-',则判断所给路径是直接路径还是间接路径 42 else 43 { 44 //直接路径,copy到path字符数组中 45 if(strncmp(argv[i],"/",1) == 0) 46 { 47 strcpy(path,argv[i]); 48 } 49 //间接路径,将当前路径与所给路径连接 50 else 51 { 52 strcat(path,"/"); 53 strcat(path,argv[i]); 54 } 55 } 56 } 57 } 58 //用于输出文件名 59 void PrintfFileName(int mode,int uid,char *name) 60 { 61 //是目录文件 62 if(S_ISDIR(mode)) 63 { 64 //文件名显示为蓝色 65 printf("33[1;34m%s 33[0m ",name); 66 } 67 //是普通文件 68 else if(S_ISREG(mode)) 69 { 70 if(mode & S_IXUSR||mode & S_IXGRP||mode & S_IXOTH) 71 { 72 if(uid==0) //属主用户,文件名显示为红色 73 printf("33[41;37m%s 33[0m ",name); 74 else //其他用户,文件名显示为绿色 75 printf("33[1;32m%s 33[0m ",name); 76 } 77 else 78 { 79 printf("%s ",name); 80 } 81 } 82 else 83 { 84 printf("%s ",name); 85 } 86 } 87 //用于输出文件详细信息 88 void PrintMoreInfo(int mode,struct stat st) 89 { //文件权限判断 90 char str[10] = {"----------"}; 91 92 if(S_ISDIR(mode)) str[0] = 'd'; 93 if(S_ISCHR(mode)) str[0] = 'c'; 94 if(S_ISBLK(mode)) str[0] = 'b'; 95 96 if(mode & S_IRUSR) str[1] = 'r'; 97 if(mode & S_IWUSR) str[2] = 'w'; 98 if(mode & S_IXUSR) str[3] = 'x'; 99 100 if(mode & S_IRGRP) str[4] = 'r'; 101 if(mode & S_IWGRP) str[5] = 'w'; 102 if(mode & S_IXGRP) str[6] = 'x'; 103 104 if(mode & S_IROTH) str[7] = 'r'; 105 if(mode & S_IWOTH) str[8] = 'w'; 106 if(mode & S_IXOTH) str[9] = 'x'; 107 108 int i = 0; 109 for(; i < 10; i++) 110 { 111 printf("%c",str[i]); 112 } 113 printf(". "); 114 printf("%ld ",st.st_nlink); 115 //输出属主 116 struct passwd *pd = getpwuid(st.st_uid); 117 assert(pd != NULL); 118 printf("%4s ",pd->pw_name); 119 //输出组用户 120 struct group *gp = getgrgid(st.st_gid); 121 assert(gp != NULL); 122 printf("%4s ",gp->gr_name); 123 //输出文件大小 124 printf("%4ld ",st.st_size); 125 //输出最近操作时间 126 struct tm * lchangetime = localtime(&(st.st_mtime)); 127 printf("%d %d %d:%d ",(lchangetime->tm_mon+1),lchangetime->tm_mday,lchangetime->tm_hour,lchangetime->tm_min); 128 } 129 130 int main(int argc,char *argv[]) 131 { 132 char path[128]={0}; 133 //获取当前路径 134 getcwd(path,127); 135 //参数分析函数 136 AnalPara(argc,argv,path); 137 //打开该目录并建立一个目录流 138 DIR *dir = opendir(path); 139 if(dir == NULL) 140 { 141 char *p = path + strlen(path); 142 while(*p != '/') 143 p--; 144 p++; 145 printf("ls:can not access %s:No such file or directory ",p); 146 exit(0); 147 } 148 //需要使用dirent结构体中的文件名和文件的inode number 149 struct dirent *dr = NULL; 150 //调用readdir函数获取该目录中的目录项 151 while((dr = readdir(dir)) != NULL) 152 { 153 //当文件名第一个字符是.时,为隐藏文件,不输出 154 if(((flag&1)==0) && (strncmp(dr->d_name,".",1) == 0)) 155 { 156 continue; 157 } 158 159 struct stat st; 160 char temp[128] = {0}; 161 strcpy(temp,path); 162 strcat(temp,"/"); 163 strcat(temp,dr->d_name); 164 stat(temp,&st); 165 //-li 166 if ((flag&2)==2) 167 { 168 //有参数i 169 if((flag&4)==4) 170 { 171 printf("%ld ",st.st_ino); 172 } 173 //有参数l 174 PrintMoreInfo(st.st_mode,st); 175 PrintfFileName(st.st_mode,st.st_uid,dr->d_name); 176 printf(" "); 177 continue; 178 } 179 //-ai 180 if((flag&4)==4) 181 { 182 printf("%ld ",dr->d_ino); 183 PrintfFileName(st.st_mode,st.st_uid,dr->d_name); 184 continue; 185 } 186 //-a 187 PrintfFileName(st.st_mode,st.st_uid,dr->d_name); 188 } 189 if(argc == 1||(argc >1&&flag == 0)) 190 printf(" "); 191 closedir(dir); 192 }
3.4 分析结果
3.4.1 ls -lai运行结果:
3.4.2 ./myls -lai运行结果:
3.4.3 分析输出格式:
第一列为文件/目录的索引编号(inode number),如果是目录,则使用dirent结构体中的d_ino获取;如果是文件,则使用stat结构体中的st_ino获取。
第二列为文件的权限,第一位的-表示不同的文件类型(普通文件,管道文件)。后面九位分别表示,该文件的属主,组用户和其他用户的读、写、执行三种不同的权限。
第三列为文件的硬链接数,如果是一个目录,则第2字段表示该目录所含子目录的个数。使用stat结构体中的st_nlink获取。
第四列为属主用户,使用stat结构体中的st_uid获取。
第五列为组用户,使用stat结构体中的st_gid获取。
第六列为文件所占用的大小,以字节为单位,如果是目录文件,则表示该目录的大小,而不是该目录下所有文件的大小。使用stat结构体中的st_size获取。
第七列为最后修改时间,使用stat结构体中的st_mtime获取。
第八列为文件名,使用dirent结构体中的d_name获取。
4 References
- https://blog.csdn.net/apollon_krj/article/details/54710135
- https://www.cnblogs.com/cherishry/p/5885107.html
- https://blog.csdn.net/jialexiao/article/details/71124930
- https://blog.csdn.net/longerzone/article/details/23870297
- https://blog.csdn.net/dream_allday/article/details/75243818
- https://blog.csdn.net/daoer_sofu/article/details/102456935