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); // 释放
}
}
测试:
当然还不够完善,没有对符号链接文件进行追踪,以及高亮输出。