八、Linux编程之递归遍历目录

八、Linux编程之递归遍历目录


一、步骤分析

实现终端指令 ls -R,即将所有内部的目录最终展开成文件,目录的目录…所有目录最终展开,并打印相应文件的大小
在这里插入图片描述
步骤
1. 判断命令行参数,获取用户要查询的目录名,通过 int argc, char *argv[] 获取,若 argc == 1,那么用户想要查询的是当前目录 ./
2. 判断用户指定的路径是否为一个目录,通过 stat() 和宏函数 S_ISDIR() 判断
3. 如果是一个目录,打开、读取、关闭:opendir()readdir()closedir(),如果是文件,打印
4. 继续判断是否存在目录,通过 stat() 和宏函数 S_ISDIR() 判断,拼接所访问目录的绝对路径:通过函数 sprintf()strcut(),获得原路径/内部目录,回到步骤 3


二、isFile()函数

先定义一个函数 void isFile(char *name){...},用于判断是目录还是普通文件,如果是普通文件,就直接打印文件名文件大小,如果是目录文件就需要进一步操作

void isFile(char *name);

// 判断文件类型
void isFile(char *name)
{
    struct stat sbuf;
    int ret = stat(name, &sbuf);
    // 出错判断
    if(ret == -1)
    {
        perror("stat error");
        exit(1);
    }

	// 宏函数判断
    if(S_ISDIR(sbuf.st_mode))
    {
        isDir(name); // 如果是一个目录,就需要进行目录相关操作
    }else
    {
        printf("%s\t\t%ld\n", name, sbuf.st_size); // 如果是普通文件,就直接打印
    }
    
}

三、isDir()函数

定义函数 void isDir(char *name){...},当所判断的文件是目录时,就需要调用该函数,进一步操作和判断,如:打开、读取、关闭目录,并判断目录内是否还有目录

由于 readdir() 读取后只有文件名,只有文件名没有路径无法正常访问文件,我们需要将路径拼接到它的前面,调用 sprintf() 函数,这个函数与 printf() 类似,只不过 printf() 是默认打印到 stdin 文件上,而 sprintf() 打印到一个字符串上

包含头文件

#include <stdio.h>

int sprintf(char *str, const char *format, …);

将格式化输出内容打印到 char *str

char *str 传出参数
const char *format 类似 printf() 中的格式化输出,例:sprintf(out, "%s是%s", a, b);
返回值 成功时返回写入的字符大小

此外,readdir() 函数还会把 ... 也读出,如下图所示:为了防止重复循环遍历,需要把 ... 过滤掉
在这里插入图片描述
方法如下

if(strcmp(sdp->d_name, ".") == 0 || strcmp(sdp->d_name, "..") == 0)
        {
            continue;
        }

void isDir(char *name){...} 函数代码如下

void isDir(char *);

// 如果是一个目录,打开目录
void isDir(char *dir)
{
    struct dirent *sdp;
    // Linux规定路径长度不能超过256
    char path[256];

    DIR *dp = opendir(dir); // 打开目录
    if(dp == NULL)
    {
        perror("opendir error");
        return;
    }

    while((sdp = readdir(dp)) != NULL) // 读目录
    {
        // 由于readdir会把 . 和 .. 也读出来,因此需要过滤
        if(strcmp(sdp->d_name, ".") == 0 || strcmp(sdp->d_name, "..") == 0)
        {
            continue;
        }

		// 字符串拼接
        sprintf(path, "%s/%s", dir, sdp->d_name);
        /* 
        	调用 isFile() 函数,判断是否为普通文件
        	如果是普通文件,则打印
        	如果是目录文件,则继续迭代
        */
        isFile(path);
    }
    printf("\n");

    int ret = closedir(dp);
    if(ret == -1)
    {
        perror("closedir error");
        return;
    }
}

四、main()主函数

如果 int argc == 1 说明没有指定目录,默认为当前目录

int main(int argc, char *argv[])
{
    if(argc == 1)
    {
        isFile(".");
    }else
    {
        isFile(argv[1]);
    }

    return 0;
}

五、运行效果

在这里插入图片描述


六、补充要点

注意:上述内容并不完整,因为命令 ls -R 是可以指定多个目录的,因此我们需要将 main() 函数做微调:

int main(int argc, char *argv[])
{
    if(argc == 1)
    {
        isFile(".");
    }else
    {
        while(--argc)
        {
            isFile(*++argv);
        }
    }

    return 0;
}

其中,while(--argc) 表示每次访问一个目录前就让 argc1,以此来统计用户输入目录的个数

其次,isFile(*++argv); 表示每次执行前先让 argv++,再取内容,那么每次循环时 isFile(*++argv); 代表的内容分别是:
isFile(argv[1]);isFile(argv[2]);isFile(argv[3]); … … 如此类推,这样就可以对每个目录进行判断了

while(--argc)
        {
            isFile(*++argv);
        }

在这里插入图片描述


七、源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>

// 函数声明
void isDir(char *);
void isFile(char *);

// 如果是一个目录,打开目录
void isDir(char *dir)
{
    struct dirent *sdp;
    // Linux规定路径长度不能超过256
    char path[256];

    DIR *dp = opendir(dir); // 打开目录
    if(dp == NULL)
    {
        perror("opendir error");
        return;
    }

    while((sdp = readdir(dp)) != NULL) // 读目录
    {
        // 由于readdir会把 . 和 .. 也读出来,因此需要过滤
        if(strcmp(sdp->d_name, ".") == 0 || strcmp(sdp->d_name, "..") == 0)
        {
            continue;
        }

		// 字符串拼接
        sprintf(path, "%s/%s", dir, sdp->d_name);
        /* 
        	调用 isFile() 函数,判断是否为普通文件
        	如果是普通文件,则打印
        	如果是目录文件,则继续迭代
        */
        isFile(path);
    }
    printf("\n");

    int ret = closedir(dp);
    if(ret == -1)
    {
        perror("closedir error");
        return;
    }
}

// 判断文件类型
void isFile(char *name)
{
    struct stat sbuf;
    int ret = stat(name, &sbuf); // 获取文件属性,判断文件类型
    // 出错判断
    if(ret == -1)
    {
        perror("stat error");
        exit(1);
    }

	// 宏函数判断是否为目录
    if(S_ISDIR(sbuf.st_mode))
    {
        isDir(name); // 如果是一个目录,就需要进行目录相关操作
    }else
    {
        printf("%s\t\t%ld\n", name, sbuf.st_size); // 如果是普通文件,就直接打印
    }
    
}

int main(int argc, char *argv[])
{
	// 判断命令行参数
    if(argc == 1)
    {
        isFile(".");
    }else
    {	// 处理用户输入的一个或多个目录
        while(--argc)
        {
            isFile(*++argv);
        }
    }

    return 0;
}

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值