[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 }
posted @ 2018-07-22 22:29  caoliu  阅读(16237)  评论(1编辑  收藏  举报