• [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. 
    ");
    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!
    ");
    34         exit(EXIT_FAILURE);
    35     }                    
    36     return 0;
    37 }
  • 相关阅读:
    设计模式
    DOS批处理脚本
    BAT 批处理脚本 教程
    spring4配置文件详解
    软件过程
    error C2440 “static_cast” 无法从“void (__thiscall C* )(void)...
    error C2065: “IDD_DIALOG1”: 未声明的标识符
    在另一个编辑器中打开
    Github 结合 Hexo 搭建轻量博客
    收藏---wordpress搭建出来的blog
  • 原文地址:https://www.cnblogs.com/usingnamespace-caoliu/p/9351773.html
Copyright © 2020-2023  润新知