操作系统第4次实验报告:文件系统
- 姓名:程开
- 学号:201821121060
- 班级:计算1812
1. 编写程序
在服务器上用Vim编写一个程序:实现Linux系统命令ls -lai
的功能,给出源代码。
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<time.h> #include<unistd.h> #include<sys/types.h> #include<dirent.h> #include<grp.h> #include<pwd.h> #include<errno.h> #include<sys/stat.h> #include<limits.h> #include<assert.h> void Rattribute(int argc,char *argv[],char *path);//读取文件属性 void PriFileName(int mode,int uid,char *name);//打印文件名 void PriExactInf(int mode,struct stat st);//打印文件详细信息 int flag = 0; int main(int argc,char *argv[]) { char path[]={"./"};//"./"表示当前工作目录,直接获取 Rattribute(argc,argv,path); DIR *dir = opendir(path); if(dir == NULL) { printf("No such file\n"); exit(0); } struct dirent *fname = NULL;//有下面几种 /*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字符 } */ //调用readdir函数获取该目录中的目录项 while((fname = readdir(dir)) != NULL) { //当文件名第一个字符是.时,为隐藏文件,不输出 if(((flag&1)==0) && (strncmp(fname->d_name,".",1) == 0)) { continue; } struct stat st; /*struct stat { mode_t st_mode; //文件对应的模式,文件,目录等 ino_t st_ino; //inode节点号 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; //文件内容对应的块数量 };*/ char temp[128] = {0}; strcpy(temp,path); strcat(temp,"/"); strcat(temp,fname->d_name); stat(temp,&st); //-li if ((flag&2)==2) { //有参数i if((flag&4)==4) { printf("%ld ",st.st_ino); } //有参数l PriExactInf(st.st_mode,st); PriFileName(st.st_mode,st.st_uid,fname->d_name); printf("\n"); continue; } //-ai if((flag&4)==4) { printf("%ld ",fname->d_ino); PriFileName(st.st_mode,st.st_uid,fname->d_name); continue; } //-a PriFileName(st.st_mode,st.st_uid,fname->d_name); } if(argc == 1||(argc >1&&flag == 0)) printf("\n"); closedir(dir); } void Rattribute(int argc,char *argv[],char *path) { int i = 0; for(i = 1;i < argc; ++i) { if(strncmp(argv[i],"-",1)==0) { if(strstr(argv[i],"a") != NULL) { flag |= 1 << 0; //-a参数显示该隐藏文件 } if(strstr(argv[i],"l") != NULL) { flag |= 1 << 1; //-l参数显示该文件的详细信息 } if(strstr(argv[i],"i") != NULL) { flag |= 1 << 2; //-i参数显示该文件的inode number } //位运算可使用一个变量同时标记多个参数是否传递 } else { //直接路径,copy到path字符数组中 if(strncmp(argv[i],"/",1) == 0) { strcpy(path,argv[i]); } //间接路径,将当前路径与所给路径连接 else { strcat(path,"/"); strcat(path,argv[i]); } } } } //用于输出文件名 void PriFileName(int mode,int uid,char *name) { //是目录文件 if(S_ISDIR(mode)) { //文件名显示为蓝色 printf("\33[1;34m%s\033[0m ",name); } //是普通文件 else if(S_ISREG(mode)) { if(mode & S_IXUSR||mode & S_IXGRP||mode & S_IXOTH) { if(uid==0) //属主用户,文件名显示为红色 printf("\33[41;37m%s\033[0m ",name); else //其他用户,文件名显示为绿色 printf("\33[1;32m%s\033[0m ",name); } else { printf("%s ",name); } } else { printf("%s ",name); } } //用于输出文件详细信息 void PriExactInf(int mode,struct stat st) { //文件权限判断 char str[10] = {"----------"}; if(S_ISDIR(mode)) str[0] = 'd'; if(S_ISCHR(mode)) str[0] = 'c'; if(S_ISBLK(mode)) str[0] = 'b'; if(mode & S_IRUSR) str[1] = 'r'; if(mode & S_IWUSR) str[2] = 'w'; if(mode & S_IXUSR) str[3] = 'x'; if(mode & S_IRGRP) str[4] = 'r'; if(mode & S_IWGRP) str[5] = 'w'; if(mode & S_IXGRP) str[6] = 'x'; if(mode & S_IROTH) str[7] = 'r'; if(mode & S_IWOTH) str[8] = 'w'; if(mode & S_IXOTH) str[9] = 'x'; int i = 0; for(; i < 10; i++) { printf("%c",str[i]); } printf(". "); printf("%ld ",st.st_nlink); //输出属主 struct passwd *pd = getpwuid(st.st_uid); assert(pd != NULL); printf("%4s ",pd->pw_name); //输出组用户 struct group *gp = getgrgid(st.st_gid); assert(gp != NULL); printf("%4s ",gp->gr_name); //输出文件大小 printf("%4ld ",st.st_size); //输出最近操作时间 struct tm * lchangetime = localtime(&(st.st_mtime)); printf("%d月%d号%d:%d ",(lchanon+1),lchangetime->tm_mday,lchangetime->tm_hour,lchangetime->tm_min); }
2. 分析运行结果
① 首先解释ls -lai命令:
-l 除文件名称外,亦将文件型态、权限、拥有者、文件大小等资讯详细列出
-a 显示所有文件及目录 (ls内定将文件名或目录名称开头为"."的视为隐藏档,不会列出)
-i 显示文件索引节点号(inode number)
②再解释下argc、argv的具体含义 :
argc和argv参数在用命令行编译程序时有用。main( int argc, char* argv[], char **env ) 中
第一个参数,int型的argc,为整型,用来统计程序运行时发送给main函数的命令行参数的个数,在VS中默认值为1。
第二个参数,char*型的argv[],为字符串数组,用来存放指向的字符串参数的指针数组,每一个元素指向一个参数。各成员含义如下:
argv[0]指向程序运行的全路径名
argv[1]指向在DOS命令行中执行程序名后的第一个字符串
argv[2]指向执行程序名后的第二个字符串
argv[3]指向执行程序名后的第三个字符串
argv[argc]为NULL
第三个参数,char**型的env,为字符串数组。env[]的每一个元素都包含ENVVAR=value形式的字符串,其中ENVVAR为环境变量,value为其对应的值。平时使用到的比较少。
从左到右解释含义
③输出文件索引节点号(inode number)
④判断文件的属性:
对文件属性进行逐一判断,符合就修改
⑤输出文件连接数
⑥输出文件所有者
通过文件对应的组输出文件所有者
⑦输出文件大小
直接输出stat结构体里面的这个
⑧输出时间
也是通过stat结构体里面的st_mtime获取
⑨输出文件名
直接打印struct dirent结构体里面的d_name即可
总结:Linux系统有很多实用的系统函数,比如用到的stat函数,能够极度方便我们解决这些问题,还有dirent.h是用于目录操作的头文件,linux 默认在/usr/include目录下。
思路就是先获取了当前的工作目录,然后再把文件信息读取到上述系统函数里面,接下来就很简单了
3. 通过该实验产生新的疑问及解答
对比了一下系统命令ls -lai
我发现系统输出的是按照文件名称排序的,所以还需要一个排序算法