Linux目录遍历实现,列出目录下文件,可使用部分参数
目標
編程實現程序list.c,列表普通磁盤文件,包括文件名和文件大小。
內容
- 對選項的處理,自行編程逐個分析命令行參數。不考慮多選項擠在一個命令行參數內的情況。
- 與ls命令類似,處理對象可以有0到多個
- 0個:列出當前目錄下所有文件
- 普通文件:列出文件
- 目錄:列出目錄下所有文件
- 實現自定義選項 -r, -a, -l, -h, -m 以及 –
- -r 遞歸方式列出子目錄(每項要含路徑,類似find的-print輸出風格,需要設計遞歸程序)
- -a 列出文件名第一個字符為圓點的普通文件(默認情況下不列出文件名首字符為圓點的文件)
- -l 后跟一整數,限定文件大小的最小值(字節)
- -h 后跟一整數,限定文件大小的最大值(字節)
- -m 后跟一整數n,限定文件的最近修改時間必須在n天內
- – 顯式地終止命令選項分析
參考資料
<sys/stat.h>頭文件
文件數據結構如下
struct stat { dev_t st_dev; // 文件所在設備ID ino_t st_ino; // 結點(inode)編號 mode_t st_mode; // 保護模式 nlink_t st_nlink; // 硬鏈接個數 uid_t st_uid; // 所有者用戶ID gid_t st_gid; // 所有者組ID dev_t st_rdev; // 設備ID(如果是特殊文件) off_t st_size; // 總體尺寸,以字節為單位 blksize_t st_blksize; // 文件系統 I/O 塊大小blkcnt_t st_blocks; // 已分配的 512B 塊個數time_t st_atime; // 上次訪問時間 time_t st_mtime; // 上次更新時間 time_t st_ctime; // 上次狀態更改時間 };主要函數有
int stat(const char *restrict pathname, struct stat *restrict buf); int fstat(int fields, struct stat *buf); int lstat(const char *restrict pathname, struct stat *restrict buf);其中stat函數的作用是獲取路徑名 path 對應的 inode 中的屬性
struct stat st; //存儲 inode 屬性信息 ret = stat(path, &st); //成功返回0,失敗返回-1還有如下定義:
#define S_IFMT 0170000 #define S_IFSOCK 0140000 #define S_IFLNK 0120000 #define S_IFREG 0100000 #define S_IFBLK 0060000 #define S_IFDIR 0040000 #define S_IFCHR 0020000 #define S_IFIFO 0010000 #define S_ISUID 0004000 #define S_ISGID 0002000 #define S_ISVTX 0001000#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) #define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) #define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) #define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)首先S_IFMT是一個掩碼,它的值是0170000(注意這里用的是八進制), 可以用來過濾出前四位表示的文件類型。
其后的連續七個分別對應套接口文件、符號鏈接文件、普通文件、塊設備、目錄、字符設備、管道,它們分別對應一個不同的值。
要判斷一個文件是不是目錄,首先通過掩碼S_IFMT把其他無關的部分置0,再與表示目錄的數值比較,從而判斷這是否是一個目錄,下面的代碼
if ((info.st_mode & S_IFMT) == S_IFDIR)printf("this is a directory");為了簡便操作,<sys/stat.h>中提供了宏來代替上述代碼,所以如果需要判斷文件是不是目錄就可以這樣:
if (S_ISDIR(info.st_mode))printf("this is a directory");實現步驟
1、實現核心功能
首先實現核心功能:遍歷列出普通磁盤文件,包括文件名和文件大小。
先定義兩個功能函數,分別用于遍歷目錄和打印文件信息,在遍歷目錄的過程中打印文件信息:
void dirwalk(char *dir, void (*func)(char *)); void print_file_info(char *name);下面來分別實現這兩個函數,先實現dirwalk函數:
/*對目錄中所有文件執行print_file_info操作*/ /*把print_file_info函數作為參數傳進去*/ void dirwalk(char *dir, void (*func)(char *)) {char name[MAX_PATH];struct dirent *dp;DIR *dfd;if ((dfd = opendir(dir)) == NULL) { //打開文件失敗fprintf(stderr, "dirwalk: can't open %s\n", dir);return;}while ((dp = readdir(dfd)) != NULL) { //讀目錄記錄項if (strcmp(dp->d_name, ".") == 0 || strcmp(dp -> d_name, "..") == 0) {continue; //跳過當前目錄以及父目錄}//目錄過長if (strlen(dir) + strlen(dp -> d_name) + 2 > sizeof(name)) {fprintf(stderr, "dirwalk : name %s %s too long\n", dir, dp->d_name);}else {sprintf(name, "%s/%s", dir, dp->d_name); //添加子目錄(*func)(name); //打印文件信息}}closedir(dfd); }然后實現print_file_info函數:
/*打印文件信息*/ void print_file_info(char *name) {struct stat stbuf;// 獲取文件狀態并儲存在stbuf結構中if (stat(name, &stbuf) == -1) {fprintf(stderr, "file size: open %s failed\n", name);return;}//如果是目錄遍歷下一級目錄if (S_ISDIR(stbuf.st_mode)) { dirwalk(name, print_file_info); }else {//不是目錄,打印文件size及nameprintf("%8ld %s\n", stbuf.st_size, name);} }到此就把核心功能實現了,接下來進一步完善功能。
2、添加命令行參數選項和錯誤響應
定義一個結構體記錄參數選項,
struct stu { //0表示未使用,1表示使用int r;int a;int l;int h;int m;int min;int max;int day; }Parameter = {0, 0, 0, 0, 0, -1, -1, -1};定義一個新的功能函數get_parameter,用來獲取參數選項,不考慮完整的錯誤分析,
/*分析參數,不具體考慮錯誤*/ void get_parameter(int argc, char *argv[]) {for (int i = 1; i < argc; i++) { //逐個分析命令行參數if (strcmp(argv[i], "-r") == 0) { // -rParameter.r = 1;}else if (strcmp(argv[i], "-a") == 0) { // -aParameter.a = 1;}else if (strcmp(argv[i], "-l") == 0) { // -l minParameter.l = 1;i++;Parameter.min = atoi(argv[i]);}else if (strcmp(argv[i], "-h") == 0) { // -l maxParameter.h = 1;i++;Parameter.max = atoi(argv[i]);}else if (strcmp(argv[i], "-m") == 0) { // -m dayParameter.m = 1;i++;Parameter.day = atoi(argv[i]);}else if (strcmp(argv[i], "--") == 0) { // --i++;if (i == argc) strcpy(path, "."); else if (i = argc - 1) strcpy(path, argv[i]);else error();break;}else if (i = argc - 1) { //不是功能參數,判斷是不是最后一項(路徑)strcpy(path, argv[i]);}else {error();}} }其中error函數為錯誤響應函數,打印參數提示信息:
void error() {printf("List information about the FILEs (the current directory by default)\n\n");printf(" -? Display this help and exit\n");printf(" -a Do not hide entries starting with .\n");printf(" -r List subdirectories recursively\n");printf(" -l <bytes> Minimum of file size\n");printf(" -h <bytes> Maximum of file size\n");printf(" -m <days> Limit file last modified time\n\n");exit(-1); }接下來修改dirwalk函數和print_file_info函數,添加參數響應功能
把dirwalk函數中打印文件的分支修改如下,添加對 -a 的處理:
else {if (dp->d_name[0] == '.' && Parameter.a == 0) //以.開頭的文件 continue;sprintf(name, "%s/%s", dir, dp->d_name);(*func)(name); }然后在print_file_info函數中添加對 -r, -l, -h, -m 的處理:
//如果是目錄 if (S_ISDIR(stbuf.st_mode)) {if (strcmp(name, path) != 0) //不顯示目標目錄的信息printf("%8ld %s/\n", stbuf.st_size, name);if (Parameter.r == 1 || strcmp(name, path) == 0) { //如果有 -r 遍歷下一級目錄dirwalk(name, print_file_info);} } else {//不是目錄,根據-l -h -m打印文件size及nameint flag = 1; //判斷是否要輸出 if (Parameter.l == 1 && stbuf.st_size < Parameter.min) flag = 0;if (Parameter.h == 1 && stbuf.st_size > Parameter.max) flag = 0;if (Parameter.m == 1) {struct timeval nowTime;gettimeofday(&nowTime, NULL);if (nowTime.tv_sec - stbuf.st_mtim.tv_sec > (time_t)(Parameter.day*86400))flag = 0;}if (flag == 1) printf("%8ld %s\n", stbuf.st_size, name); }這樣代碼就完成啦,完整代碼見附錄
3、測試
使用命令vi list.c編寫代碼并保存后,利用命令gcc list.c -o list編譯成可執行文件,開始測試
- 先執行ls命令,再執行簡單的./list命令,對比正確
- 執行ls -a命令,再執行./list -a命令,對比正確
- 執行./list -r命令,結果正確
- 執行./list -r -l 4000 -h 20000 temp命令,結果正確
- 執行./list -- -a命令,結果顯示不存在該文件,正確
代碼
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <dirent.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/time.h>#define MAX_PATH 512 //最大文件長度定義為512struct stu { //0表示未使用,1表示使用int r;int a;int l;int h;int m;int min;int max;int day; }Parameter = {0, 0, 0, 0, 0, -1, -1, -1};char path[MAX_PATH] = ".";void dirwalk(char *dir, void (*func)(char *)); void print_file_info(char *name); void get_parameter(int argc, char *argv[]); void error();/*對目錄中所有文件執行print_file_info操作*/ /*把print_file_info函數作為參數傳進去*/ void dirwalk(char *dir, void (*func)(char *)) {char name[MAX_PATH];struct dirent *dp;DIR *dfd;if ((dfd = opendir(dir)) == NULL) {fprintf(stderr, "dirwalk: can't open %s\n", dir);return;}while ((dp = readdir(dfd)) != NULL) { //讀目錄記錄項if (strcmp(dp->d_name, ".") == 0 || strcmp(dp -> d_name, "..") == 0) {continue; //跳過當前目錄以及父目錄}if (strlen(dir) + strlen(dp -> d_name) + 2 > sizeof(name)) {fprintf(stderr, "dirwalk : name %s %s too long\n", dir, dp->d_name);}else {if (dp->d_name[0] == '.' && Parameter.a == 0) //以.開頭的文件 continue;sprintf(name, "%s/%s", dir, dp->d_name);(*func)(name);}}closedir(dfd); }/*打印文件信息*/ void print_file_info(char *name) {struct stat stbuf;// 獲取文件狀態并儲存在stbuf結構中if (stat(name, &stbuf) == -1) {fprintf(stderr, "file size: open %s failed\n", name);return;}//如果是目錄if (S_ISDIR(stbuf.st_mode)) {if (strcmp(name, path) != 0)printf("%8ld %s/\n", stbuf.st_size, name);if (Parameter.r == 1 || strcmp(name, path) == 0) { //如果有 -r 遍歷下一級目錄dirwalk(name, print_file_info);}}else {//不是目錄,根據-l -h -m打印文件size及nameint flag = 1; //判斷是否要輸出 if (Parameter.l == 1 && stbuf.st_size < Parameter.min) flag = 0;if (Parameter.h == 1 && stbuf.st_size > Parameter.max) flag = 0;if (Parameter.m == 1) {struct timeval nowTime;gettimeofday(&nowTime, NULL);if (nowTime.tv_sec - stbuf.st_mtim.tv_sec > (time_t)(Parameter.day*86400))flag = 0;}if (flag == 1) printf("%8ld %s\n", stbuf.st_size, name);} }/*分析參數,不具體考慮錯誤*/ void get_parameter(int argc, char *argv[]) {for (int i = 1; i < argc; i++) {if (strcmp(argv[i], "-r") == 0) {Parameter.r = 1;}else if (strcmp(argv[i], "-a") == 0) {Parameter.a = 1;}else if (strcmp(argv[i], "-l") == 0) {Parameter.l = 1;i++;Parameter.min = atoi(argv[i]);}else if (strcmp(argv[i], "-h") == 0) {Parameter.h = 1;i++;Parameter.max = atoi(argv[i]);}else if (strcmp(argv[i], "-m") == 0) {Parameter.m = 1;i++;Parameter.day = atoi(argv[i]);}else if (strcmp(argv[i], "--") == 0) {i++;if (i == argc) strcpy(path, "."); else if (i = argc - 1) strcpy(path, argv[i]);else error();break;}else if (i = argc - 1) {strcpy(path, argv[i]);}else {error();}} }void error() {printf("List information about the FILEs (the current directory by default)\n\n");printf(" -? Display this help and exit\n");printf(" -a Do not hide entries starting with .\n");printf(" -r List subdirectories recursively\n");printf(" -l <bytes> Minimum of file size\n");printf(" -h <bytes> Maximum of file size\n");printf(" -m <days> Limit file last modified time\n\n");exit(-1); }int main(int argc, char *argv[]) {get_parameter(argc, argv);printf("file size file name\n");print_file_info(path);return 0; }參考來源:
https://blog.csdn.net/astrotycoon/article/details/8679676
https://blog.csdn.net/y396397735/article/details/50640919
總結
以上是生活随笔為你收集整理的Linux目录遍历实现,列出目录下文件,可使用部分参数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java互联网架构师入门进阶之路
- 下一篇: Linux基本总结