[Linux环境编程] Linux系统命令“ls -l”的实现
Linux系统命令“ls -l”的实现
一、基本概念
1、“ls -l”的意义
以长格式显示目录下的内容列表。输出的信息从左到右依次包括文件名,文件类型、权限模式、硬连接数、所有者、组、文件大小和文件的最后修改时间等。
例:-rw-rw-r-- 1 using using 3102 7月 22 17:06 test.c
drwxrwxr-x 2 using using 4096 7月 22 18:39 testdir
lrwxrwxrwx 1 using using 17 7月 22 18:43 shared -> /media/sf_shared/
其中深蓝色为目录文件,天蓝色为软连接文件(具体颜色和vimrc配置有关)。
第一字段:首字母代表的是文件类型 ,其中"-"为普通文件、"d"为目录文件、"c"为字符设备文件、"b"为块设备文件、"p"为管道文件、"l"为链接文件、"s"为socket文件。“rwx”分别代表拥有读、写和执行权限,“-”代表无对应权限。三个“rwx”依次代表文件所有者、文件所有者所在用户组、其它用户对文件拥有的权限。
第二字段:文件硬连接数量
第三字段:文件拥有者
第四字段:文件拥有者所在组
第五字段:文件大小(以字节为单位)
第六字段:文件最后更改时间
第七字段:文件名(若为链接文件则追加显示其链接的原文件的路径)
二、重要函数与结构体
1、目录操作函数
1 #include <sys/types.h> 2 #include <dirent.h> 3 4 DIR *opendir(const char *name); 5 DIR *fdopendir(int fd); 6 7 8 #include <dirent.h> 9 10 struct dirent *readdir(DIR *dirp); 11 12 struct dirent { 13 ino_t d_ino; /* inode number */ 14 off_t d_off; /* offset to the next dirent */ 15 unsigned short d_reclen; /* length of this record */ 16 unsigned char d_type; /* type of file; not supported by all file system types */ 17 char d_name[256]; /* filename */ 18 };
2、 获取文件信息
这里必须使用int lstat(const char *path, struct stat *buf);函数,否则在处理链接文件时会将其链接的原文件作为处理对象,而不是它本身。
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <unistd.h> 4 5 int stat(const char *path, struct stat *buf); 6 int fstat(int fd, struct stat *buf); 7 int lstat(const char *path, struct stat *buf); 8 9 struct stat { 10 dev_t st_dev; /* ID of device containing file */ 11 ino_t st_ino; /* inode number */ 12 mode_t st_mode; /* protection */ 13 nlink_t st_nlink; /* number of hard links */ 14 uid_t st_uid; /* user ID of owner */ 15 gid_t st_gid; /* group ID of owner */ 16 dev_t st_rdev; /* device ID (if special file) */ 17 off_t st_size; /* total size, in bytes */ 18 blksize_t st_blksize; /* blocksize for file system I/O */ 19 blkcnt_t st_blocks; /* number of 512B blocks allocated */ 20 time_t st_atime; /* time of last access */ 21 time_t st_mtime; /* time of last modification */ 22 time_t st_ctime; /* time of last status change */ 23 };
3、 文件类型及权限的判断
1 The following POSIX macros are defined to check the file type using the st_mode field: 2 3 S_ISREG(m) is it a regular file? 4 S_ISDIR(m) directory? 5 S_ISCHR(m) character device? 6 S_ISBLK(m) block device? 7 S_ISFIFO(m) FIFO (named pipe)? 8 S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.) 9 S_ISSOCK(m) socket? (Not in POSIX.1-1996.) 10 11 12 The following flags are defined for the st_mode field: 13 14 S_IFMT 0170000 bit mask for the file type bit fields 15 S_IFSOCK 0140000 socket 16 S_IFLNK 0120000 symbolic link 17 S_IFREG 0100000 regular file 18 S_IFBLK 0060000 block device 19 S_IFDIR 0040000 directory 20 S_IFCHR 0020000 character device 21 S_IFIFO 0010000 FIFO 22 S_ISUID 0004000 set UID bit 23 S_ISGID 0002000 set-group-ID bit (see below) 24 S_ISVTX 0001000 sticky bit (see below) 25 S_IRWXU 00700 mask for file owner permissions 26 S_IRUSR 00400 owner has read permission 27 S_IWUSR 00200 owner has write permission 28 S_IXUSR 00100 owner has execute permission 29 S_IRWXG 00070 mask for group permissions 30 S_IRGRP 00040 group has read permission 31 S_IWGRP 00020 group has write permission 32 S_IXGRP 00010 group has execute permission 33 S_IRWXO 00007 mask for permissions for others (not in group) 34 S_IROTH 00004 others have read permission 35 S_IWOTH 00002 others have write permission 36 S_IXOTH 00001 others have execute permission
4、文件用户ID与用户所在组ID的转换
1 #include <sys/types.h> 2 #include <pwd.h> 3 4 struct passwd *getpwnam(const char *name); 5 struct passwd *getpwuid(uid_t uid); 6 int getpwnam_r(const char *name, struct passwd *pwd,char *buf, size_t buflen, struct passwd **result); 7 int getpwuid_r(uid_t uid, struct passwd *pwd,char *buf, size_t buflen, struct passwd **result); 8 9 10 The passwd structure is defined in <pwd.h> as follows: 11 12 struct passwd { 13 char *pw_name; /* username */ 14 char *pw_passwd; /* user password */ 15 uid_t pw_uid; /* user ID */ 16 gid_t pw_gid; /* group ID */ 17 char *pw_gecos; /* user information */ 18 char *pw_dir; /* home directory */ 19 char *pw_shell; /* shell program */ 20 };
1 #include <sys/types.h> 2 #include <grp.h> 3 4 struct group *getgrnam(const char *name); 5 struct group *getgrgid(gid_t gid); 6 int getgrnam_r(const char *name, struct group *grp,char *buf, size_t buflen, struct group **result); 7 int getgrgid_r(gid_t gid, struct group *grp,char *buf, size_t buflen, struct group **result); 8 9 10 The group structure is defined in <grp.h> as follows: 11 12 struct group { 13 char *gr_name; /* group name */ 14 char *gr_passwd; /* group password */ 15 gid_t gr_gid; /* group ID */ 16 char **gr_mem; /* group members */ 17 };
5、文件最后修改时间
文件最后修改时间可以通过tm结构体接收localtime函数返回值来获取。
1 #include <time.h> 2 3 struct tm *localtime(const time_t *timep); 4 struct tm *localtime_r(const time_t *timep, struct tm *result); 5 6 7 Broken-down time is stored in the structure tm which is defined in <time.h> as follows: 8 9 struct tm { 10 int tm_sec; /* seconds */ 11 int tm_min; /* minutes */ 12 int tm_hour; /* hours */ 13 int tm_mday; /* day of the month */ 14 int tm_mon; /* month */ 15 int tm_year; /* year */ 16 int tm_wday; /* day of the week */ 17 int tm_yday; /* day in the year */ 18 int tm_isdst; /* daylight saving time */ 19 };
三、执行结果及对比
四、总结
总的来说,实现“ls -l”功能所涉及的特殊结构体较多,基础知识考察较多,需要构建很多小函数,较为繁杂,但逻辑结构简单,没有什么需要特别留意的地方,总体难度较低。
本博是在博友“Apollon_krj”的一篇博客“Linux&C编程之Linux系统命令“ls -l”的简单实现”的基础上改进完成。总体沿用了原有思路和框架,做了以下改良:
1. 可以处理软连接文件(原处理链接文件所链接的原文件);
2. 当输入“myls -l”指令时默认显示当前目录下文件的详细信息(原报错);
3. 指令、代码优化。
但目前暂未实现总用量/total、模糊匹配和彩字显示功能,有兴趣的朋友可以尝试一下。
五、实现代码
1、myls.h
1 #ifndef _MYLS_H_ 2 #define _MYLS_H_ 3 4 #include<stdio.h> 5 #include<stdlib.h> 6 #include<string.h> 7 #include<unistd.h> 8 #include<dirent.h> 9 #include<sys/stat.h> 10 #include<sys/types.h> 11 #include<fcntl.h> 12 #include<time.h> 13 #include<pwd.h> 14 #include<grp.h> 15 16 // 处理错误 17 void error_printf(const char* ); 18 19 // 处理路径下的文件 20 void list_dir(const char* ); 21 void list_message(const char* , const struct stat*); 22 23 // 所显示的文件信息 24 void file_type(const struct stat* ); 25 void file_power(const struct stat* ); 26 // printf st_nlink 27 void file_id(const struct stat* ); 28 // printf st_size 29 void file_mtime(const struct stat* ); 30 // printf filename 31 void link_printf(const char* ); 32 33 #endif//_MYLS_H_
2、 myls.c
1 #include "myls.h" 2 3 // 处理错误 4 void error_printf(const char* funname) 5 { 6 perror(funname); 7 exit(EXIT_FAILURE); 8 /* 9 * EXIT_SUCCESS和EXIT_FAILURE是两个常量。 10 * EXIT_SUCCESS=0,EXIT_FAILURE=1。 11 * 0表示程序寿终正寝,1表示死于非命。 12 */ 13 } 14 15 // 读取路径下的文件 16 void list_dir(const char* pathname) 17 { 18 DIR* ret_opendir = opendir(pathname); // 打开目录"pathname" 19 if(ret_opendir == NULL) 20 error_printf("opendir"); 21 22 int ret_chdir = chdir(pathname); // 改变工作目录至"pathname",便于stat函数的使用 23 if(ret_chdir == -1) 24 error_printf("chdir"); 25 26 struct dirent* ret_readdir = NULL; // 定义readdir函数返回的结构体变量 27 while(ret_readdir = readdir(ret_opendir)) // 判断是否读取到目录尾 28 { 29 char* filename = ret_readdir->d_name; // 获取文件名 30 struct stat file_message = {}; // 定义stat函数返回的结构体变量 31 int ret_stat = lstat(filename, &file_message); // 获取文件信息 32 if(ret_stat == -1) // stat读取文件错误则输出提示信息 33 printf("%s error!", filename); 34 else if(strcmp(filename,".") && strcmp(filename,"..")) // 不输出当前目录与上一级目录 35 list_message(filename, &file_message); 36 } 37 } 38 39 // 打印所读取文件的信息 40 void list_message(const char* filename, const struct stat* file_message) 41 { 42 file_type(file_message); // 判断打印文件类型 43 file_power(file_message); // 判断并打印文件权限 44 printf("%d ", file_message->st_nlink); // 打印硬链接数 45 file_id(file_message); // 转换并打印用户id与组id 46 printf("%5ld ", file_message->st_size); // 打印文件大小 47 file_mtime(file_message); // 打印文件最后修改时间 48 printf("%s ", filename); // 打印文件名 49 if(S_ISLNK(file_message->st_mode)) // 如果是软链接文件,打印其指向的位置 50 link_printf(filename); 51 puts(""); 52 } 53 54 55 // 所显示的文件信息 56 void file_type(const struct stat* file_message) 57 { 58 //mode_t mode = (*get_message).st_mode; 59 mode_t mode = file_message->st_mode; 60 61 if (S_ISREG(mode)) printf("-"); // 普通文件 62 else if(S_ISDIR(mode)) printf("d"); // 目录文件 63 else if(S_ISCHR(mode)) printf("c"); // 字符设备文件 64 else if(S_ISBLK(mode)) printf("b"); // 块设备文件 65 else if(S_ISFIFO(mode)) printf("p"); // 管道文件 66 else if(S_ISLNK(mode)) printf("l"); // 链接文件 67 else printf("s"); // socket文件 68 } 69 70 void file_power(const struct stat* file_message) 71 { 72 mode_t mode = file_message->st_mode; 73 74 // 判断USR权限 75 printf("%c", mode&S_IRUSR?'r':'-'); 76 printf("%c", mode&S_IWUSR?'w':'-'); 77 printf("%c", mode&S_IXUSR?'x':'-'); 78 79 // 判断GRP权限 80 printf("%c", mode&S_IRGRP?'r':'-'); 81 printf("%c", mode&S_IWGRP?'w':'-'); 82 printf("%c", mode&S_IXGRP?'x':'-'); 83 84 // 判断OTH权限 85 printf("%c", mode&S_IROTH?'r':'-'); 86 printf("%c", mode&S_IWOTH?'w':'-'); 87 printf("%c ", mode&S_IXOTH?'x':'-'); 88 } 89 90 void file_id(const struct stat* file_message) 91 { 92 // 根据用户id获取用户名 93 struct passwd* pwd; 94 pwd = getpwuid(file_message->st_uid); 95 printf("%s ",pwd->pw_name); 96 97 // 根据组id获取组名 98 struct group* grp; 99 grp = getgrgid(file_message->st_gid); 100 printf("%s ",grp->gr_name); 101 102 #if 0 103 struct passwd 104 { 105 char * pw_name; /* Username, POSIX.1 */ 106 char * pw_passwd; /* Password */ 107 __uid_t pw_uid; /* User ID, POSIX.1 */ 108 __gid_t pw_gid; /* Group ID, POSIX.1 */ 109 char * pw_gecos; /* Real Name or Comment field */ 110 char * pw_dir; /* Home directory, POSIX.1 */ 111 char * pw_shell; /* Shell Program, POSIX.1 */ 112 }; 113 114 struct group 115 { 116 char *gr_name; /* Group name */ 117 char *gr_passwd; /* password */ 118 __gid_t gr_gid; /* Group ID */ 119 char **gr_mem; /* Member list */ 120 } 121 #endif//0 122 } 123 124 void file_mtime(const struct stat* file_message) 125 { 126 struct tm* t = localtime(&file_message->st_mtime); 127 printf("%2d月 %2d %02d:%02d ", t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min); 128 } 129 130 void link_printf(const char* filename) 131 { 132 char buf[1024] = "123"; 133 if(0 == readlink(filename, buf, sizeof(buf))) 134 error_printf("readlink"); 135 printf("-> %s ",buf); 136 }
3、main_myls.c
1 #include "myls.h" 2 3 4 int main(const char argc, const char** argv) 5 { 6 char path[1024] = {}; 7 8 if(argc == 2 && !(strcmp(argv[1],"-l"))) // 判断命令格式 9 strcpy(path,"./"); 10 else if(argc != 3) 11 { 12 printf("usage:ls -l pathname. \n"); 13 exit(EXIT_FAILURE); 14 } 15 else 16 strcpy(path,argv[2]); 17 18 if(!(strcmp(argv[1],"-l"))) 19 { 20 struct stat file_message = {}; 21 int ret_stat = lstat(path, &file_message); 22 23 if(ret_stat == -1) 24 error_printf("stat"); 25 26 if(S_ISDIR(file_message.st_mode)) // 判断是否为目录 27 list_dir(path); 28 else 29 list_message(path, &file_message); 30 } 31 else 32 { 33 printf("error in main!\n"); 34 exit(EXIT_FAILURE); 35 } 36 return 0; 37 }