linux 遍历目录文件 list命令 c语言实现
任务描述
源代码(list.c)
#include <time.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#define DEBUG_STD_OUTPUT
#define atoi_64(__str__) strtoll(__str__, NULL, 10)
#define IS_NUMBER_STR(__STRING__) strspn(__STRING__, "0123456789") == strlen(__STRING__)
#if defined(DEBUG_STD_OUTPUT)
#define LOG_IF_ENABLE(...) \
printf(__VA_ARGS__);
#else
#define LOG_IF_ENABLE(__LOG__)
#endif
#define FMT_RED(__STR__) "\033[;31m"__STR__"\033[0m"
#define FMT_BLUE(__STR__) "\033[;34m"__STR__"\033[0m"
#define FMT_CYAN(__STR__) "\033[;36m"__STR__"\033[0m"
#define FMT_GREEN(__STR__) "\033[;32m"__STR__"\033[0m"
#define FMT_BLACK(__STR__) "\033[;30m"__STR__"\033[0m"
#define FMT_YELLOW(__STR__) "\033[;37m"__STR__"\033[0m"
enum LIST_COMMAND_ARG_INFO {
ARG_NO_ATTACHED_BIT = 0,
ARG_SHOW_RECURSIVE_BIT = 1,
ARG_SHOW_HIDDEN_BIT = 1 << 1,
ARG_SET_MINIMUM_BIT = 1 << 2,
ARG_SET_MAXIMUM_BIT = 1 << 3,
ARG_SET_CHANGETIME_LMT_BIT = 1 << 4
};
typedef struct {
enum LIST_COMMAND_ARG_INFO argInfo;
uint32_t minimumSize;
uint32_t maximumSize;
uint32_t modifiedLmt;
} ListCommandInfo;
typedef struct dirent* DirentPtr;
extern int getcwd(char*, int);
static ListCommandInfo dealWithArguments(int argc, char* argv[]);
static void handleDir(const char* dirPath, const ListCommandInfo* pInfo);
static void displayDirInfo(DIR* pDIR, const ListCommandInfo* pInfo, const char* curPath);
static void handleFile(struct stat* pFileInfo, const char* pFilename, const ListCommandInfo* pCmdInfo);
static char listPathRoot[PATH_MAX] = {0};
int main(int argc, char* argv[]) {
// 获取工作目录到全局变量
getcwd(listPathRoot, sizeof(listPathRoot));
/**
* 要想扩展list命令到支持文件夹&文件为参数,请自行判别命令行附加参数值为目录或文件后取出
* 调用 handleDir / handleFile 即可
*/
// 拿到命令行参数
ListCommandInfo cmdInfo = dealWithArguments(argc, argv);
// 从pwd开始执行
handleDir(listPathRoot, &cmdInfo);
return 0;
}
void displayDirInfo(DIR* pDIR, const ListCommandInfo* pInfo, const char* curPath) {
char path[PATH_MAX] = {0};
DirentPtr pDirent = NULL; // 定义readdir函数返回的结构体变量
// 判断是否读取到目录尾
while((pDirent = readdir(pDIR)) > 0) {
// 定义stat函数返回的结构体变量
struct stat fileMessage = {};
// 获取文件名
char* filename = pDirent->d_name;
// 处理path字符串
{
int end = 0;
while (curPath[end]) {
end++;
}
strcpy(path, curPath);
if (curPath[end - 1] != '/')
strcat(path, "/");
strcat(path, filename);
// 获取文件信息
int statStatus = lstat(path, &fileMessage);
// stat读取文件错误则输出提示信息
if(statStatus == -1) {
return;
// exit(-5);
}
}
// 判断是否输出当前目录、上一级目录与隐藏文件
if (pInfo->argInfo & ARG_SHOW_HIDDEN_BIT) {
if (S_ISDIR(fileMessage.st_mode)) {
if (filename[0] != '.')
printf(FMT_BLUE("%s")"\t", filename);
else
printf(FMT_RED("%s")"\t", filename);
} else {
handleFile(&fileMessage, filename, pInfo);
}
} else {
if (filename[0] != '.') {
if (S_ISDIR(fileMessage.st_mode)) {
printf(FMT_BLUE("%s")"\t", filename);
} else {
handleFile(&fileMessage, filename, pInfo);
}
}
}
}
// 将文件流的指针拨回起始位置(因为用到了readdir函数))
rewinddir(pDIR);
putchar('\n'); putchar('\n');
}
static void handleDir(const char* dirPath, const ListCommandInfo* pInfo) {
// 每进入一个path就先打印全部信息,然后再进入新的path,递归实现深度优先
char nextpath[PATH_MAX];
DIR *pDIR = opendir(dirPath);
if (pDIR == NULL) {
fprintf(stderr, "FAILED TO OPEN DIR\n");
exit(-4);
}
// 输出当前目录路径
printf("%s:\n", dirPath);
// 显示目录内部信息
displayDirInfo(pDIR, pInfo, dirPath);
DirentPtr pDirent = NULL;
// 判断是否读取到目录尾,若读到则while中断
while((pDirent = readdir(pDIR)) > 0) {
// 获取文件名
char* filename = pDirent->d_name;
// 优化显示路径(处理"./test/"与"./test")
int end = 0;
while (dirPath[end]) {
end++;
}
strcpy(nextpath, dirPath);
if (dirPath[end - 1] != '/')
strcat(nextpath, "/");
strcat(nextpath, filename);
// 定义stat函数返回的结构体变量
struct stat fileMessage = {};
// 获取文件信息
int statInfo = lstat(nextpath, &fileMessage);
// stat读取文件错误则输出提示信息
if(statInfo == -1) {
return;
// exit(-5);
}
// 筛选"." & ".."与隐藏目录
else {
if (pInfo->argInfo & ARG_SHOW_RECURSIVE_BIT) {
if (strlen(filename) >= 2) {
if (S_ISDIR(fileMessage.st_mode)) {
if (filename[0] == '.' &&
filename[1] != '.' &&
(pInfo->argInfo & ARG_SHOW_HIDDEN_BIT)
) {
handleDir(nextpath, pInfo);
} else {
if (filename[0] != '.') {
handleDir(nextpath, pInfo);
}
}
}
} else {
if (S_ISDIR(fileMessage.st_mode)) {
if (filename[0] != '.') {
handleDir(nextpath, pInfo);
}
}
}
}
}
}
closedir(pDIR);
}
// 用来优化文件打印样式 & 检测文件属性与命令行参数的对应关系
void handleFile(struct stat* pFileInfo, const char* pFilename, const ListCommandInfo* pCmdInfo) {
// 文件容量 (bytes)
off_t fileSize = pFileInfo->st_size;
// 最后修改时间
time_t mtime = pFileInfo->st_mtim.tv_sec;
time_t curTime = time(NULL);
int printFlag = 1;
if ((pCmdInfo->argInfo & ARG_SET_MINIMUM_BIT)) {
if (fileSize < pCmdInfo->minimumSize) {
printFlag = 0;
}
}
if ((pCmdInfo->argInfo & ARG_SET_MAXIMUM_BIT)) {
if (fileSize > pCmdInfo->maximumSize) {
printFlag = 0;
}
}
// 以天为单位
if ((pCmdInfo->argInfo & ARG_SET_CHANGETIME_LMT_BIT)) {
if ((curTime - mtime) / 86400 > pCmdInfo->modifiedLmt) {
printFlag = 0;
}
}
if (printFlag) {
// 如果为二进制 & 可执行文件
if (strchr(pFilename, '.') == NULL) {
printf(FMT_GREEN("%s")"\t", pFilename);
} else {
printf("%s\t", pFilename);
}
}
}
#define GET_ARG_VALUE(__ARGUMENT__) \
if (IS_NUMBER_STR(argv[i])) { \
__ARGUMENT__ = atoi_64(argv[i]); \
} else { \
fprintf( \
stderr, \
"INPUT ARGUMENT ["#__ARGUMENT__ "]: \"%s\" " \
"CANNOT BE CONVERTED INTO UNSIGNED INTEGER\n\n", argv[i]); \
exit(-3); \
}
#define INC_CHECK_INDEX \
i++; \
if (i >= argc) { \
exit(-2); \
}
// 处理命令行参数
ListCommandInfo dealWithArguments(int argc, char* argv[]) {
// just for fun
if (argc < 1) {
exit(-1);
}
putchar('\n');
int i = 0;
ListCommandInfo cmdInfo = {};
enum LIST_COMMAND_ARG_INFO argInfo = ARG_NO_ATTACHED_BIT;
uint32_t minimumSize = 0,
maximumSize = 0 - 1,
modifiedLmt = 0 - 1;
for (i = 1; i < argc; i++) {
// 递归处理模式
if (strcmp(argv[i], "-r") == 0) {
if ((argInfo & ARG_SHOW_RECURSIVE_BIT) > 0) {
fprintf(stderr, "FAILED TO REBIND ARG: -r\n\n");
exit(-2);
} else {
argInfo = argInfo | ARG_SHOW_RECURSIVE_BIT;
}
// 处理隐藏文件
} else if (strcmp(argv[i], "-a") == 0) {
if ((argInfo & ARG_SHOW_HIDDEN_BIT) > 0) {
fprintf(stderr, "FAILED TO REBIND ARG: -a\n\n");
exit(-2);
} else {
argInfo = argInfo | ARG_SHOW_HIDDEN_BIT;
}
// 引入文件大小最小值参数
} else if (strcmp(argv[i], "-l") == 0) {
if ((argInfo & ARG_SET_MINIMUM_BIT) > 0) {
fprintf(stderr, "FAILED TO REBIND ARG: -l\n\n");
exit(-2);
} else {
INC_CHECK_INDEX;
GET_ARG_VALUE(minimumSize);
argInfo = argInfo | ARG_SET_MINIMUM_BIT;
}
// 引入文件大小最大值参数
} else if (strcmp(argv[i], "-h") == 0) {
if ((argInfo & ARG_SET_MAXIMUM_BIT) > 0) {
fprintf(stderr, "FAILED TO REBIND ARG: -h\n\n");
exit(-2);
} else {
INC_CHECK_INDEX;
GET_ARG_VALUE(maximumSize);
argInfo = argInfo | ARG_SET_MAXIMUM_BIT;
}
// 引入限定文件最近修改时间参数
} else if (strcmp(argv[i], "-m") == 0) {
if ((argInfo & ARG_SET_CHANGETIME_LMT_BIT) > 0) {
fprintf(stderr, "FAILED TO REBIND ARG: -m\n\n");
exit(-2);
} else {
INC_CHECK_INDEX;
GET_ARG_VALUE(modifiedLmt);
argInfo = argInfo | ARG_SET_CHANGETIME_LMT_BIT;
}
} else if (strcmp(argv[i], "--") == 0) {
break;
} else {
fprintf(stderr, "PLEASE CHECK THIS INPUT COMMAND ARGUMENTS: \"%s\"\n\n", argv[i]);
}
}
cmdInfo.argInfo = argInfo;
cmdInfo.maximumSize = maximumSize;
cmdInfo.modifiedLmt = modifiedLmt;
cmdInfo.minimumSize = minimumSize;
if (maximumSize < minimumSize) {
fprintf(stderr, "[MAX SIZE:%u bytes] CAN NOT BE LESS THAN [MIN SIZE:%u bytes]!\n\n", maximumSize, minimumSize);
exit(-6);
}
#if defined(DEBUG_STD_OUTPUT)
char inputArguments[512] = "ENABLED MODE: \n";
if (argInfo & ARG_SHOW_RECURSIVE_BIT) {
sprintf(inputArguments + strlen(inputArguments), "\t[SHOW RECURSIVE]\n");
}
if (argInfo & ARG_SHOW_HIDDEN_BIT) {
sprintf(inputArguments + strlen(inputArguments), "\t[SHOW HIDDEN]\n");
}
if (argInfo & ARG_SET_MINIMUM_BIT) {
sprintf(inputArguments + strlen(inputArguments), "\t[WITH MINIMUM FILE SIZE(%u bytes)]\n", minimumSize);
}
if (argInfo & ARG_SET_MAXIMUM_BIT) {
sprintf(inputArguments + strlen(inputArguments), "\t[WITH MAXIMUM FILE SIZE(%u bytes)]\n", maximumSize);
}
if (argInfo & ARG_SET_CHANGETIME_LMT_BIT) {
sprintf(inputArguments + strlen(inputArguments), "\t[WITH MAXIMUM MODIFY TIME LIMIT(%u days)]\n", modifiedLmt);
}
printf("ABSOLUTE EXECUTE PATH: %s\n%s", listPathRoot, inputArguments);
#endif
putchar('\n');
return cmdInfo;
}
测试样例(从上到下由旧到新,实现存在不完整或差异,欢迎补充)