日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

Linux目录遍历实现,列出目录下文件,可使用部分参数

發布時間:2023/12/15 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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目录遍历实现,列出目录下文件,可使用部分参数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。