模拟实现 tree 指令

本文介绍了一个简单的自制Linux下的tree命令实现方法,通过遍历目录结构并以树状形式展示文件和目录,包括使用队列存储文件信息、递归读取目录内容等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

linux下的 tree 指令:
这里写图片描述
打印所有文件和目录,默认不打印隐藏文件
在这里,我只实现默认方式,并且文件只区分出需要进行遍历的目录文件,其他文件统一直接进行输出。

要实现这个指令
首先要清楚 Linux 下的文件系统,以及如何对目录文件进行读取。

文件表结构:
这里写图片描述

首先 我们要对输入的文件进行判断,判断其是否是目录文件。
如果输入的文件是普通文件,则直接输出,如果是目录文件则开始进行遍历。

读取文件属性

extern int lstat (const char* filename, struct stat* buf);
lstat 相比较于 stat,它不跟随符号链接,同样是将读取到的信息放在 buf 中。

在对首次输入的文件的类型进行判断:
如果是普通文件则直接输出这个文件名即可。
如果是目录文件,则对这个目录文件的内容进行遍历。
此时需要用到函数

DIR* opendir(const char* dirname);
// 打开一个目录文件,并将信息保存在返回的 DIR 类型中。
// 对象 DIR 对用户层是透明的,可不用关注其实现细节。
int closedir(DIR *dirp);
// 关闭锁打开的目录流

当走到打开目录流时的这一步时,已经可以判定这个文件是目录文件了,此时便可以对其内容进行遍历读取,此时需要函数

struct dirent *readdir(DIR* dirp);
// 读取目录内容
// 每一次调用 readdir(),其将返回指向下一个目录条目的指针。
// 如果达到目录结尾或检测无效操作,将返回 NULL 指针。

而其中的 dirent 的结构为

struct dirent{
    long    d_ino;     // inode 值
    _kernal_off_t  d_off;   // 从目录开始到当前目录条的距离
    unsigned short d_reclen;  // 文件名的空间大小
    char    d_name[256];   //文件名 以'/0'结束

};

在这里,我们使用一个队列来保存所有的文件信息。
整体上是碰到一个目录文件,则进行递归遍历,并将每个文件添加到队列尾部。
因为提前设置了 level(层数),所以在输出的时候是按顺序,从第一层文件的第一个到最后一个的所有信息依次输出。这样就和 tree 指令大致较为类似了。

队列结构:

typedef struct node{   // 节点类型
    struct node *next;
    unsigned int level;  // 层数
    char *name;    // 文件名
    char fullname[256];   // 绝对路径/相对路径
}filenode;

typedef struct head{   //所要模拟的队列头节点类型
    struct node* head;   //head
    struct node* rear;   //last
}headnode;

实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>

typedef struct node{   // 节点类型
    struct node *next;
    unsigned int level;  // 层数
    char *name;    // 文件名
    char fullname[256];   // 绝对路径/相对路径
}filenode;

typedef struct head{   //所要模拟的队列头节点类型
    struct node* head;   //head
    struct node* rear;   //last
}headnode;


void Print(filenode * cur)  //输出每个文件
{
    int i;
    printf("|");
    for(i=0; i<cur->level; i++){
        printf("    ");
    }
    if( i == 0)
        printf("--- ");
    else
        printf("|-- ");
    printf("%-4s\n",cur->name);
}

void Free(filenode * cur)   //释放
{
    free(cur->name);
    free(cur);
}
void Print_dir(headnode * link_stack)  // 输出
{
    filenode * cur = link_stack->head;
    filenode * next = cur->next;
    while(next != NULL){
        Print(cur);
        Free(cur);
        cur = next;
        next = next->next;
    }
}

void read_dir_link(char* dirname, headnode *link_stack ,int level)   //对目录文件进行读取
{
    if(dirname == NULL)
        return;

    struct stat stat_dst;
    if(lstat(dirname, &stat_dst) == -1)   // 读取文件属性
        perror("lstat"),exit(1);
    if(!S_ISDIR(stat_dst.st_mode)){   // 如果文件不是目录文件则直接添加到队列末尾
            filenode* link = NULL;
            link = (filenode*)malloc(sizeof(filenode));
            // 节点初始化
            link->next = NULL;
            link->level = level;
            link->name = (char*)malloc(strlen(link_stack->rear->name) + 1);
            memset(link->name, 0x00, strlen(link_stack->rear->name));
            memcpy(link->name, link_stack->rear->name, strlen(link_stack->rear->name));
            sprintf(link->fullname, "%s/%s\0", dirname,link->name); // 保存绝对路径/相对路径
            // 插入队列中
            if(link_stack->head == NULL){ 
                link_stack->head = link;
                link_stack->rear = link;
            }else{
                link_stack->rear->next = link;
                link_stack->rear = link;
            }
            return;
    }
    // 是目录文件
    DIR *dirp = opendir(dirname);  // 打开目录流
    if(dirp == NULL)
        perror("opendir"),exit(1);
    struct dirent *dp;
    while((dp = readdir(dirp)) != NULL)  // 读取目录文件信息
    {
        if( *(dp->d_name) == '.')
            continue;
            filenode* link = NULL;
            // 节点初始化
            link = (filenode*)malloc(sizeof(filenode));
            link->next = NULL;
            link->level = level;
            link->name = (char*)malloc(strlen(dp->d_name) + 1);
            memset(link->name, 0x00, strlen(dp->d_name) + 1);
            memcpy(link->name, dp->d_name, strlen(dp->d_name));
            sprintf(link->fullname, "%s/%s\0", dirname,link->name);
            // 插入队列
            if(link_stack->head == NULL){
                link_stack->head = link;
                link_stack->rear = link;
            }else{
                link_stack->rear->next = link;
                link_stack->rear = link;
            }
            struct stat stat_src;
            lstat(link->fullname, &stat_src);
            if(S_ISDIR(stat_src.st_mode)){  // 如果是目录文件,则进行递归遍历,并且层数 + 1
                read_dir_link(link->fullname, link_stack, level + 1);
            }
        }
    closedir(dirp); // 关闭目录流
}

void read_dir(char *dirname, headnode* link_stack, int level)
{
    if(dirname == NULL)
        return;
    headnode * ret = NULL;
    read_dir_link(dirname, link_stack, level);
}

void Str(char *dirname, int len)  // 对第一个输入文件的进行格式输出
{
    char* dst = (char*)malloc(len + 2);
    memset(dst, 0x00, len + 2);
    memcpy(dst, dirname, len);
    char *cur = dst;
    char *next = dst;
    while( *next != '\0'){
        if( (*next) == '/' ){
            cur = ++next;
        }
        next++;
    }
    printf("%s\n", cur);
    free(dst);
}
int main(int argc, char*argv[] ){
    if(argc != 2)  // 参数判断
        perror("argc != 2"),exit(1);

    struct stat stat_src;
    if( lstat(argv[1], &stat_src) == -1 )
        perror("lstat"),exit(1);
    // 读取文件信息
    if( S_ISREG(stat_src.st_mode) )  // 如果是普通文件则可以直接输出
        printf("%s\n", argv[1]),exit(1);
    else if( S_ISDIR(stat_src.st_mode) ){  // 目录文件
        headnode * link_stack = (headnode *)malloc(sizeof(headnode));

        if(link_stack == NULL)
            perror("malloc"),exit(1);
        // 头节点初始化
        link_stack->head = NULL;
        link_stack->rear = NULL;
        int len = strlen(argv[1]);
        Str(argv[1], len);
        // 进行遍历
        read_dir(argv[1], link_stack, 0);
        Print_dir(link_stack); // 输出
        free(link_stack); // 释放
        }
}

测试:
这里写图片描述
当然还不够完善,没有对符号链接文件进行追踪,以及高亮输出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值