🎈4. 实现 ls -l filename命令
我们可以通过stat函数来实现 ls -l 命令的功能,下面我们实现查看指定文件的 ls -l 命令,即
ls -l filename
实现代码如下
/************************************************************ >File Name : mls.c >Author : QQ >Company : QQ >Create Time: 2022年05月15日 星期日 16时29分01秒 >实现目标:-rw-r--r--. 2 root root 11 5月 14 15:02 file.txt ************************************************************/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <time.h> #include <pwd.h> #include <grp.h> int main(int argc, char* argv[]) { if(argc < 2) { printf("not found filename\n"); return -1; } /*通过stat函数获取文件信息*/ struct stat fstatus; stat(argv[1], &fstatus); /*穿透*/ /*lstat(argv[1], &fstatus); 非穿透 */ /*解析文件信息,st_mode st_uid st_gid time*/ /*-rw-r--r--.*/ char stmode[11]; memset(stmode, '-', sizeof(stmode)); /*使用宏来判断文件属性*/ if(S_ISREG(fstatus.st_mode)) /* regular file */ { stmode[0] = '-'; } if(S_ISDIR(fstatus.st_mode)) { stmode[0] = 'd'; } if(S_ISCHR(fstatus.st_mode)) { stmode[0] = 'c'; } if(S_ISBLK(fstatus.st_mode)) { stmode[0] = 'b'; } if(S_ISFIFO(fstatus.st_mode)) { stmode[0] = 'p'; } if(S_ISLNK(fstatus.st_mode)) { stmode[0] = 'l'; } if(S_ISSOCK(fstatus.st_mode)) { stmode[0] = 's'; } /*解析权限位*/ if(fstatus.st_mode & S_IRUSR) {/*为真表示拥有该权限,否则无权限,因为整块内存已初始化为 - 所以不需要else分支*/ stmode[1] = 'r'; } if(fstatus.st_mode & S_IWUSR) { stmode[2] = 'w'; } if(fstatus.st_mode & S_IXUSR) { stmode[3] = 'x'; } if(fstatus.st_mode & S_IRGRP) { stmode[4] = 'r'; } if(fstatus.st_mode & S_IWGRP) { stmode[5] = 'w'; } if(fstatus.st_mode & S_IXGRP) { stmode[6] = 'x'; } if(fstatus.st_mode & S_IROTH) { stmode[7] = 'r'; } if(fstatus.st_mode & S_IWOTH) { stmode[8] = 'w'; } if(fstatus.st_mode & S_IXOTH) { stmode[9] = 'x'; } stmode[10] = '\0'; /*获取时间 localtime() 函数(非系统调用) *原型:struct tm *localtime(const time_t *timep); *参数:time_t类型,struct stat中time_t st_atime,这里应该是文件访问时间 *返回:struct tm { int tm_sec; seconds (0-60 ) int tm_min; minutes (0-59) int tm_hour; hours (0-23) int tm_mday; day of the month (1-31) int tm_mon; month (0-11) int tm_year; year (-1900) 如果要求实际年份,应加上1990 int tm_wday; day of the week sunday=0 int tm_yday; day in the year int tm_isdst; daylight saving time }; */ struct tm* filetime = localtime(&fstatus.st_atim.tv_sec); char timebuf[20] = {0}; sprintf(timebuf, "%d月 %d %02d:%02d", \ filetime->tm_mon + 1, \ filetime->tm_mday, \ filetime->tm_hour, \ filetime->tm_min); /*打印格式 -rw-r--r--. 2 root root 11 5月 14 15:02 file.txt*/ printf("%s %ld %s %s %ld %s %s\n", \ stmode, \ fstatus.st_nlink, \ getpwuid(fstatus.st_uid)->pw_name, \ getgrgid(fstatus.st_gid)->gr_name, \ fstatus.st_size, \ timebuf, \ argv[1]); /* 两个函数(非系统调用) struct passwd *getpwuid(uid_t uid); 根据uid获取用户信息 struct passwd { char *pw_name; username char *pw_passwd; user password uid_t pw_uid; user ID gid_t pw_gid; group ID char *pw_gecos; real name char *pw_dir; home directory char *pw_shell; shell program }; struct group *getgrgid(gid_t gid); 根据gid获取组信息 struct group { char *gr_name; group name char *gr_passwd; group password gid_t gr_gid; group ID char **gr_mem; group members }; */ return 0; }
测试一下效果
🎈5. 穿透与非穿透
上面介绍了stat函数并通过stat函数实现了 ls -l 命令的功能。我们上面演示了使用自己实现的 ./mls 查看文件信息,假如说使用 ./mls 查看一个链接文件是什么效果呢,下面演示一下。
通过对比我们可以看到,符号链接(软链接)file.txt.soft的实际大小是8,但是我们自己实现的 ./mls 命令显示的大小是11。实际上,原因是这样的,我们在实现 ./mls 命令的时候是基于stat函数来获取文件信息的,stat函数有一个特性就是在获取链接文件信息的时候会进行穿透,去追溯符号链接的源文件,也就是说我们通过上面的命令 ./mls file.txt.soft 获取到的大小实际上是源文件file.txt的大小,我们可以验证一下。
那么我们自己如何实现获取符号链接的实际大小呢,这就用到了非穿透函数lstat,只要把上面代码实现中的函数调用stat替换为lstat就可以了,下面测试一下。
lstat(argv[1], &fstatus);