[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 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM