300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Linux目录遍历实现 列出目录下文件 可使用部分参数

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

时间:2020-08-01 21:17:32

相关推荐

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

参考来源:

/astrotycoon/article/details/8679676

/y396397735/article/details/50640919

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。