[Linux環境編程] Linux系統命令“rm -rf”的實現


Linux系統命令“rm -rf”的實現

 

一、基本概念

1、“rm -rf”的意義

  -r或-R:遞歸處理,將指定目錄下的所有文件與子目錄一並處理;

  -f:強制刪除文件或目錄;

  由於rm命令只能刪除空的目錄,因此當我們需要刪除一個非空目錄時可以使用“rm -rf”命令,通過遞歸的方式先將該目錄中的文件刪除使其成為一個空的目錄后再將其刪除,從而達到刪除一個非空目錄的效果。

 

 

二、重要函數與結構體

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.)

 

4、當前用戶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 
 7 
 8        The passwd structure is defined in <pwd.h> as follows:
 9 
10            struct passwd {
11                char   *pw_name;       /* username */
12                char   *pw_passwd;     /* user password */
13                uid_t   pw_uid;        /* user ID */
14                gid_t   pw_gid;        /* group ID */
15                char   *pw_gecos;      /* user information */
16                char   *pw_dir;        /* home directory */
17                char   *pw_shell;      /* shell program */
18            };

 

5、 刪除文件函數

  該函數比較危險,測試代碼時建議先注釋,通過printf函數查看刪除文件路徑,確認后再使用。

1        #include <stdio.h>
2 
3        int remove(const char *pathname);
4 
5        On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.

 

 

三、執行結果及對比

1、 執行前

 

2、執行效果

 

3、 執行后

 

 

四、總結

  總的來說,實現“rm -rf”功能所涉及的知識面不廣,代碼有較多的重復片段,可以在實現“ls -R”功能的基礎上進行,但程序細節部分較多,而且程序測試的風險極高,總體難度一般,最重要的是要做好安全備份工作。細節部分具體可以參考程序中的注釋,這里主要說明下前期的安全准備工作:

  1. 進行Linux系統的備份,Ubuntu虛擬機系統可以通過Virtual box進行系統備份;

  2. 在進入刪除函數之前加一道“確認”流程,以免誤操作;

  3. 將所有remove函數注釋,先輸出所刪除文件路徑,所有路徑確認無誤后再取消注釋。

 

 

五、實現代碼

1、 myrm.h 

 1 #ifndef _MYRM_H_
 2 #define _MYRM_H_
 3 
 4 #include <stdio.h>
 5 #include <stdlib.h>
 6 #include <string.h>
 7 #include <limits.h>
 8 #include <unistd.h>
 9 #include <dirent.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <fcntl.h>
13 #include <time.h>
14 #include <pwd.h>
15 #include <grp.h>
16 #include <stdbool.h>
17 
18 // 該頭文件包含了自定義函數getch(),其功能是:接收單個鍵盤按鍵值,無需結束標志enter
19 #include "getch.h" 
20 
21 // 處理錯誤
22 void error_printf(const char* );
23 
24 // 刪除路徑下的文件
25 void rm_dir(const char* );
26 
27 // 判斷目錄下文件的擁有者是否是程序調用者
28 bool ismine_dir(const char* );
29 bool ismine(const char* );
30 
31 #endif//_MYRM_H_

 

2、myrm.c

 1 #include "myrm.h"
 2 
 3 
 4 int main(const char argc, const char** argv)
 5 {
 6     char path[PATH_MAX+1] = {};
 7 
 8     if(argc == 2 && !(strcmp(argv[1],"-rf"))) // 判斷命令格式
 9         strcpy(path,".");
10     else if(argc != 3)
11     {
12         printf("格式有誤! \n");
13         exit(EXIT_FAILURE);
14     }
15     else
16         strcpy(path,argv[2]);
17 
18     if(!(strcmp(argv[1],"-rf")))
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         {
28             printf("路徑[%s]下的目錄將會被刪除,按‘enter’確認,其他任意鍵取消\n",path); // 防止誤操作
29             if(10 == getch()) // 自定義函數getch(),其功能是:接收單個鍵盤按鍵值,無需結束標志enter
30                 rm_dir(path);
31             else
32                 return 0;
33         }
34         else
35             printf("It is not dir!");
36     }
37     else
38     {
39         printf("error in main!\n");
40         exit(EXIT_FAILURE);
41     }                    
42     return 0;
43 }

 

3、main_myrm.c

  1 #include "myrm.h"
  2 
  3 // 處理錯誤
  4 void error_printf(const char* funname)
  5 {
  6     perror(funname);
  7     exit(EXIT_FAILURE);
  8 }
  9 
 10 // 刪除路徑下的文件
 11 void rm_dir(const char* pathname)
 12 {
 13     if(!ismine_dir(pathname)) // 判斷該路徑目錄中包含的文件是否全部屬於程序調用者
 14     {
 15         printf("權限不足,無法刪除!");
 16         return;
 17     }
 18 
 19     char nextpath[PATH_MAX+1];
 20 
 21     DIR* ret_opendir = opendir(pathname); // 打開目錄"pathname"
 22     if(ret_opendir == NULL)
 23         error_printf("opendir");
 24 
 25     struct dirent* ret_readdir = NULL; // 定義readdir函數返回的結構體變量
 26     while(ret_readdir = readdir(ret_opendir)) // 判斷是否讀取到目錄尾
 27     {
 28         char* filename = ret_readdir->d_name; // 獲取文件名
 29 
 30         int end = 0; // 優化路徑 (其實無論幾個“/”疊加都不會影響路徑)
 31         while(pathname[end])
 32             end++;
 33         strcpy(nextpath,pathname);
 34         if(pathname[end-1] != '/')
 35             strcat(nextpath,"/");
 36         strcat(nextpath,filename);
 37 
 38         struct stat file_message = {}; // 定義stat函數返回的結構體變量
 39         int ret_stat = lstat(nextpath, &file_message); // 獲取文件信息
 40         if(ret_stat == -1) // stat讀取文件錯誤則輸出提示信息
 41         {
 42             printf("%s error!", filename);
 43         }
 44         // 注意屏蔽當前目錄和上一級目錄,但不能用filename[0]='.'來判斷,這樣會導致無法刪除隱藏文件,從而導致目錄文件刪除失敗
 45         else if(S_ISDIR(file_message.st_mode)  && strcmp(filename,".") && strcmp(filename,"..")) 
 46         {
 47             rm_dir(nextpath);
 48             printf("delete dir :%s\n",nextpath);
 49             remove(nextpath);
 50         }
 51         else if(strcmp(filename,".") && strcmp(filename,"..")) // 同上
 52         {
 53             struct passwd* pwd = getpwuid(file_message.st_uid);
 54             
 55             if(pwd->pw_uid == getuid()) // 再次檢查,雙重保險
 56             {
 57                 printf("delete file:%s\n",nextpath);
 58                 remove(nextpath);
 59             }
 60         }
 61     }
 62     closedir(ret_opendir);
 63     printf("delete dir :%s\n",pathname);
 64     remove(pathname); // 刪除完目錄中的文件后將自身刪除
 65 }
 66 
 67 bool ismine_dir(const char* pathname)
 68 {
 69     char nextpath[PATH_MAX+1];
 70         
 71     if(!ismine(pathname))
 72         return false;
 73 
 74     DIR* ret_opendir = opendir(pathname); // 打開目錄"pathname"
 75     if(ret_opendir == NULL)
 76         error_printf("opendir");
 77 
 78     struct dirent* ret_readdir = NULL; // 定義readdir函數返回的結構體變量
 79     while(ret_readdir = readdir(ret_opendir)) // 判斷是否讀取到目錄尾
 80     {
 81         char* filename = ret_readdir->d_name; // 獲取文件名
 82 
 83         int end = 0; // 優化路徑
 84         while(pathname[end])
 85             end++;
 86         strcpy(nextpath,pathname);
 87         if(pathname[end-1] != '/')
 88             strcat(nextpath,"/");
 89         strcat(nextpath,filename);
 90 
 91         struct stat file_message = {}; // 定義stat函數返回的結構體變量
 92         int ret_stat = lstat(nextpath, &file_message); // 獲取文件信息
 93         if(ret_stat == -1) // stat讀取文件錯誤則輸出提示信息
 94         {
 95             printf("%s error!", filename);
 96         }
 97         else if(S_ISDIR(file_message.st_mode)  && strcmp(filename,".") && strcmp(filename,".."))
 98         {
 99             if(!ismine_dir(nextpath))
100                 return false;
101         }
102         else if(strcmp(filename,".") && strcmp(filename,".."))
103         {
104             if(!ismine(nextpath))
105                 return false;
106         }
107     }
108     closedir(ret_opendir);
109     return true;
110 }
111 
112 bool ismine(const char* pathname)
113 {
114     struct stat file_message = {}; // 定義stat函數返回的結構體變量
115     int ret_stat = lstat(pathname, &file_message); // 獲取文件信息
116     if(ret_stat == -1) // stat讀取文件錯誤則輸出提示信息
117     {
118         printf("stat error!");
119     }
120     struct passwd* pwd = getpwuid(file_message.st_uid);    
121     if(pwd->pw_uid == getuid()) // 判斷該文件是否屬於程序調用者
122         return true;
123     else
124         return false;
125 }


免責聲明!

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



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