[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
12struct 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
5int stat(const char *path, struct stat *buf);
6int fstat(int fd, struct stat *buf);
7        int lstat(const char *path, struct stat *buf);
8
9struct 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    0004000set UID bit
23            S_ISGID    0002000set-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
4struct passwd *getpwnam(const char *name);
5        struct passwd *getpwuid(uid_t uid);
6int getpwnam_r(const char *name, struct passwd *pwd,char *buf, size_t buflen, struct passwd **result); 7int 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
12struct passwd {
13                char  *pw_name;      /* username */
14char  *pw_passwd;    /* user password */
15                uid_t  pw_uid;        /* user ID */
16                gid_t  pw_gid;        /* group ID */
17char  *pw_gecos;      /* user information */
18char  *pw_dir;        /* home directory */
19char  *pw_shell;      /* shell program */
20            };
1        #include <sys/types.h>
2        #include <grp.h>
3
4struct group *getgrnam(const char *name);
5        struct group *getgrgid(gid_t gid);printf输出格式linux
6int getgrnam_r(const char *name, struct group *grp,char *buf, size_t buflen, struct group **result); 7int 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
12struct group {
13                char  *gr_name;      /* group name */
14char  *gr_passwd;    /* group password */
15                gid_t  gr_gid;        /* group ID */
16char  **gr_mem;        /* group members */
17            };
5、⽂件最后修改时间
  ⽂件最后修改时间可以通过tm结构体接收localtime函数返回值来获取。
1        #include <time.h>
2
3        struct tm *localtime(const time_t *timep);
4struct 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”功能所涉及的特殊结构体较多,基础知识考察较多,需要构建很多⼩函数,较为繁杂,但逻辑结构简单,没有什么需要特别留意的地⽅,总体难度较低。
  本博是在博友“”的⼀篇博客“”的基础上改进完成。总体沿⽤了原有思路和框架,做了以下改良:
  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// 处理错误
17void error_printf(const char* );
18
19// 处理路径下的⽂件
20void list_dir(const char* );
21void list_message(const char* , const struct stat*);
22
23// 所显⽰的⽂件信息
24void file_type(const struct stat* );
25void file_power(const struct stat* );
26// printf st_nlink
27void file_id(const struct stat* );
28// printf st_size
29void file_mtime(const struct stat* );
30// printf filename
31void link_printf(const char* );
32
33#endif//_MYLS_H_
2、 myls.c
1 #include "myls.h"
2
3// 处理错误
4void 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// 读取路径下的⽂件
16void list_dir(const char* pathname)
17 {
18    DIR* ret_opendir = opendir(pathname); // 打开⽬录"pathname"
19if(ret_opendir == NULL)
20        error_printf("opendir");
21
22int ret_chdir = chdir(pathname); // 改变⼯作⽬录⾄"pathname",便于stat函数的使⽤
23if(ret_chdir == -1)
24        error_printf("chdir");
25
26struct dirent* ret_readdir = NULL; // 定义readdir函数返回的结构体变量
27while(ret_readdir = readdir(ret_opendir)) // 判断是否读取到⽬录尾
28    {
29char* filename = ret_readdir->d_name; // 获取⽂件名
30struct stat file_message = {}; // 定义stat函数返回的结构体变量
31int ret_stat = lstat(filename, &file_message); // 获取⽂件信息
32if(ret_stat == -1) // stat读取⽂件错误则输出提⽰信息
33            printf("%s error!", filename);
34else if(strcmp(filename,".") && strcmp(filename,"..")) // 不输出当前⽬录与上⼀级⽬录
35            list_message(filename, &file_message);
36    }
37 }
38
39// 打印所读取⽂件的信息
40void 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); // 打印⽂件名
49if(S_ISLNK(file_message->st_mode)) // 如果是软链接⽂件,打印其指向的位置
50        link_printf(filename);
51    puts("");
52 }
53
54
55// 所显⽰的⽂件信息
56void 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
61if    (S_ISREG(mode))  printf("-"); // 普通⽂件
62else if(S_ISDIR(mode))  printf("d"); // ⽬录⽂件
63else if(S_ISCHR(mode))  printf("c"); // 字符设备⽂件
64else if(S_ISBLK(mode))  printf("b"); // 块设备⽂件
65else if(S_ISFIFO(mode)) printf("p"); // 管道⽂件
66else if(S_ISLNK(mode))  printf("l"); // 链接⽂件
67else                    printf("s"); // socket⽂件
68 }
69
70void 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
90void file_id(const struct stat* file_message)
91 {
92// 根据⽤户id获取⽤户名
93struct passwd* pwd;
94    pwd = getpwuid(file_message->st_uid);
95    printf("%s ",pwd->pw_name);
96
97// 根据组id获取组名
98struct group* grp;
99    grp = getgrgid(file_message->st_gid);