目标

编程实现程序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目录遍历实现,列出目录下文件,可使用部分参数相关推荐

  1. linux循环目录下目录,Shell循环列出目录下所有文件或查找目录下包含指定字符串的文件...

    一.循环列出目录下所有文件 #!/bin/bash function frfile() { local basefolder=$1 echo $basefolder #local filename f ...

  2. LINUX下用C语言历遍目录 C语言列出目录

    (被考了一题遍历目录,连需要的系统库函数我都不知道...打击中...) 小羽给了一个答案: #include<stdio.h> #include<dirent.h> int m ...

  3. php web 目录遍历,php的目录遍历操作

    * 目录遍历操作: * 1. 传统过程函数: opendir(),readdir(),closedir() * 2. 目录扫描器: scandir() * 第一种方式: 传统目录函数 * 第一步: o ...

  4. linux 目录比较meld,Meld:Ubuntu下文件/目录对比

    Meld允许用户查看文件.目录间的变化.很容易移植到Gedit下,方便用户使用. 安装Meld Meld默认在Ubuntu官方源中,在Ubuntu软件中心搜索一下就可以安装,或者使用命令: sudo ...

  5. linux重装系统怎么保存home下文件,Linux系统重装与还原

    当初第一次装ubuntu系统时,对文件系统的构成还不太了解,所以在分区的时候给home的分区特别小,导致后期软件都装不进去.说磁盘已满.所以想对系统又一次分区. 上网找了资料,都说用GParted这个 ...

  6. Linux解压.tgz,.tar.gz文件(tar详细参数使用介绍)

    一.Linux操作.tgz文件 1.解压.tgz文件: (1)解压到当前文件夹 命令:tar zxvf  文件名.tgz -C ./ eg:tar zxvf demo.tgz -C ./ (2)解压到 ...

  7. Linux运维之linux下文件与目录管理

    一.实验环境(rhel7.0版本) 主机环境:rhel7.0 各主机信息 主机名 IP server 172.25.254.1 二.Linux的系统结构 1.Linux系统是一个根文件系统,是一个倒树 ...

  8. [转自LinuxSir.Org]Linux 文件和目录管理之列出、删除、复制、移动及改名

    目录 1.21 列出目录下所有文件,包含或不包含隐藏文件,-a 参数及-l参数的运用: 1.22 仅查看目录的属性的-d参数的运用示例: 1.23 查看文件或目录的inode节点,-i参数的运用: 1 ...

  9. 快速上手Linux核心命令(三):文件和目录操作命令

    文件和目录操作命令 前言 cd 切换目录 pwd 显示当前路径 ls 显示目录下内容及相关属性信息 mkdir 创建目录 tree 以树形结构显示目录下的内容 touch 创建空白文件或改变文件的时间 ...

  10. portswigger 目录遍历文件上传

    目录 <1> 目录遍历 什么是目录遍历? 通过目录遍历读取任意文件 (1) 去除了多余 URL 解码的遍历序列 (2) 非递归剥离的遍历序列 如何防止目录遍历攻击 <2> 文件 ...

最新文章

  1. Serverless:为我们到底带来了什么
  2. MySQL中EXPLAIN命令详解
  3. android o preview 3,Android O Preview 之 通知渠道(Notification Channels)
  4. Yii的查询后缀限定
  5. 火蚁机器人_「适度偷懒提高整体效率:火蚁工作方式启发机器人群组协作」8月23日...
  6. 一步一步写算法(之 A*算法)
  7. Python链表学习
  8. 计算机二级软件excel打不开怎么办,excel文件打不开的9种情况和解决办法
  9. win10通过OneDrive实现办公室的电脑和家里电脑重要数据同步
  10. “黑盒工坊”,轻松管理《魔兽世界》插件!
  11. CMD 打开各个浏览器命令
  12. ant design vue table 高度自适应_对比1万2千个Vue.js开源项目发现最实用的 TOP45!火速拿来用!...
  13. Aria2打造离线网盘
  14. Excel2013利用图标集在单元格内加上各式各样的图标来表示状态
  15. 启用和禁用excel 中的加载项
  16. r软件 linux,分享|Linux 上好用的 R 语言 IDE
  17. Linux下配置DNS服务
  18. PAT乙级 | 1069 微博转发抽奖 (20分)
  19. 大内存加速网站应用方案
  20. 苹果电脑黑屏后重装系统的方法,可以保护所有数据,不使用u盘和移动硬盘

热门文章

  1. java 7.0下载_Java jre 7.0
  2. iOS中常用的四种数据持久化方法
  3. 你以为AlphaGo只是下围棋厉害?不,它还能用来优化金融交易策略参数
  4. numpy 读取txt为array 一行搞定
  5. opensuse x64下编译Ice源码(以编译c++为例)
  6. JavaScript 总结几个提高性能知识点
  7. 保存cisco设备配置
  8. IT围城,你是想挤进来还是想离开
  9. EL toString()功能,对象转换字符串
  10. linux树形目录结构存放,Linux目录结构