C++实现简单的ls命令及其原理
作者:Luyoungs
这篇文章主要介绍了C++实现简单的ls命令及其原理,C++实现ls命令可通过调用系统函数实现读取目录中的文件名和属性,再通过标准输出进行显示,需要的朋友可以参考下
思维导图
准备工作
对控制参数的处理
一共有 7 个可选参数,分别是-a、-l、-R、-t、-r、-i、-s
,这些参数可以相互自由组合,因此可以设计一种机制,就是直接把它们全部用循环一次性做或运算,得到一个参数标记Vec。
// 标记: -a、-l、-R、-t、-r、-i、-s 参数(向量分量) #define a 0b1000000 #define l 0b0100000 #define R 0b0010000 #define t 0b0001000 #define r 0b0000100 #define I 0b0000010 #define s 0b0000001 // 向量 int Vec = 0;
而 Vec 可以使用全局变量,这样可以避免写函数时不断地给函数参数加入地址参数,使得更加代码整洁,更直观。
对dir参数的处理
同理,依然可以设计一个全局容器,不断地把 dirname
扔进去:
char* dirname[4096 * 128]; int dirlen = 0;
而对于 filename
也是一样的,但在每次遍历一个dir
之前,就得filename
容器做重置处理:
char* filenames[4096 * 128]; int file_cnt = 0;
函数实现
void tags_cal(int argc, char* argv[]) { for (int i = 1; i < argc; i++) { if (argv[i][0] != '-') { // 只接受以'-'开头的参数,其它参数要么错误,要么是文件夹名称或文件名 char* tempdirname = (char*)malloc(sizeof(char) * 4096); strcpy(tempdirname, argv[i]); dirname[dirlen++] = tempdirname; } else { int len = strlen(argv[i]); for (int j = 1; j < len; j++) { switch (argv[i][j]) { case 'a': Vec |= a; break; case 'l': Vec |= l; break; case 'R': Vec |= R; break; case 't': Vec |= t; break; case 'r': Vec |= r; break; case 'i': Vec |= I; break; case 's': Vec |= s; break; default: fprintf(stderr, "%c参数错误!\n", argv[i][j]); break; } } } } if (dirlen == 0) { dirlen = 1; char* tempdirname = (char*)malloc(sizeof(char) * 2048); strcpy(tempdirname, "."); dirname[0] = tempdirname; } }
这里需要注意的是,如果dirlen == 0
,说明我们的命令并没有加参数,默认是对当前文件夹进行操作,因此需要重新对 dirlen
赋值为 1
,然后把 dirname[0]
置为"."
。
实现
我们上一步成功得到了,dirnname
、dirlen
,这样就可以逐个dirname[i]
进行处理了!
void do_myls() { for (int i = 0; i < dirlen; i++) { if (do_name(dirname[i]) == -1) { continue; } // 且自动字典排序 if ((Vec & t) == t) { // 时间排序 do_t(filenames); } if ((Vec & r) == r) { // 逆序 do_r(filenames, file_cnt); } printf("当前路径:\"%s\"\n", dirname[i]); int tag = 0; // 换行 for (int j = 0; j < file_cnt; j++) { // 拼凑文件名 char path[4096] = {0}; strcpy(path, dirname[i]); int len = strlen(dirname[i]); strcpy(&path[len], "/"); strcpy(&path[len + 1], filenames[j]); tag++; if ((Vec & a) == 0) { if ((strcmp(filenames[j], ".") == 0 || strcmp(filenames[j], "..") == 0) || filenames[j][0] == '.') { continue; } } struct stat info; stat(path, &info); // 拉进 info if (S_ISDIR(info.st_mode) && ((Vec & R) == R)) { // 如果是目录,那就直接拉进 dirnames:"dirname/filename" char* tempdirname = (char*)malloc(sizeof(char) * 4096); strcpy(tempdirname, dirname[i]); int len = strlen(tempdirname); strcpy(&tempdirname[len], "/"); strcpy(&tempdirname[len + 1], filenames[j]); dirname[dirlen++] = tempdirname; } if ((Vec & I) == I) { do_i(path); } if ((Vec & s) == s) { do_s(path); } if ((Vec & l) == 0) { if (S_ISDIR(info.st_mode)) // 判断是否为目录 { printf(GREEN "%s\t" NONE, filenames[j]); } else { printf(BLUE "%s\t" NONE, filenames[j]); } } if ((Vec & l) == l) { void mode_to_letters(); char modestr[11]; mode_to_letters(info.st_mode, modestr); printf("%s ", modestr); printf("%4d ", (int)info.st_nlink); printf("%-8s ", uid_to_name(info.st_uid)); printf("%-8s ", gid_to_name(info.st_gid)); printf("%8ld ", (long)info.st_size); printf("%.12s ", ctime(&info.st_mtime)); if (S_ISDIR(info.st_mode)) // 判断是否为目录 { printf(GREEN "%s\t" NONE, filenames[j]); } else { printf(BLUE "%s\t" NONE, filenames[j]); } printf("\n"); } if ((tag % 5 == 0) && ((Vec & l) == 0)) { printf("\n"); } } // 清空容器 for (int k = 0; k < file_cnt; k++) { memset(filenames[k], 4096, '\0'); } file_cnt = 0; } }
这里最关键的就是对-R
参数的处理,因为我们的整体框架并不适合做函数的递归,因此我们可以在判断某个 filename
是一个 dir
之后,就可以把它加入到 dirname
中,并且把 dirlen++
,这样就在逻辑上实现了遍历,这里也充分利用了全局变量的优势:牵一发而动全身。
struct stat info; stat(path, &info); // 拉进 info if (S_ISDIR(info.st_mode) && ((Vec & R) == R)) { // 如果是目录,那就直接拉进 dirnames:"dirname/filename" char* tempdirname = (char*)malloc(sizeof(char) * 4096); strcpy(tempdirname, dirname[i]); int len = strlen(tempdirname); strcpy(&tempdirname[len], "/"); strcpy(&tempdirname[len + 1], filenames[j]); dirname[dirlen++] = tempdirname; }
而其它的功能性函数实现起来就很简单了,就不累赘了。
完整代码
#include <dirent.h> #include <grp.h> #include <pwd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <time.h> #include <unistd.h> // 标记: -a、-l、-R、-t、-r、-i、-s 参数(向量分量) #define a 0b1000000 #define l 0b0100000 #define R 0b0010000 #define t 0b0001000 #define r 0b0000100 #define I 0b0000010 #define s 0b0000001 // 颜色宏 #define NONE "\033[m" #define GREEN "\033[0;32;32m" #define BLUE "\033[0;32;34m" // 函数声明 void tags_cal(int argc, char* argv[]); void restored_ls(struct dirent* cur_item); void sort(char** filenames, int start, int end); void do_r(char** filenames, int file_cnt); int partition(char** filenames, int start, int end); void swap(char** s1, char** s2); int compare(char* s1, char* s2); char* uid_to_name(uid_t); char* gid_to_name(gid_t); void mode_to_letters(int, char[]); char* uid_to_name(uid_t); // ********函数声明******** void do_i(char filename[]); void do_s(char filename[]); int do_name(char dirname[]); void do_myls(); void do_t(char** filenames); int Vec = 0; char* dirname[4096 * 128]; int dirlen = 0; char* filenames[4096 * 128]; int file_cnt = 0; int main(int argc, char* argv[]) { tags_cal(argc, argv); do_myls(); return 0; } void do_myls() { for (int i = 0; i < dirlen; i++) { if (do_name(dirname[i]) == -1) { continue; } // 且自动字典排序 if ((Vec & t) == t) { // 时间排序 do_t(filenames); } if ((Vec & r) == r) { // 逆序 do_r(filenames, file_cnt); } printf("当前路径:\"%s\"\n", dirname[i]); int tag = 0; // 换行 for (int j = 0; j < file_cnt; j++) { // 拼凑文件名 char path[4096] = {0}; strcpy(path, dirname[i]); int len = strlen(dirname[i]); strcpy(&path[len], "/"); strcpy(&path[len + 1], filenames[j]); tag++; if ((Vec & a) == 0) { if ((strcmp(filenames[j], ".") == 0 || strcmp(filenames[j], "..") == 0) || filenames[j][0] == '.') { continue; } } struct stat info; stat(path, &info); // 拉进 info if (S_ISDIR(info.st_mode) && ((Vec & R) == R)) { // 如果是目录,那就直接拉进 dirnames:"dirname/filename" char* tempdirname = (char*)malloc(sizeof(char) * 4096); strcpy(tempdirname, dirname[i]); int len = strlen(tempdirname); strcpy(&tempdirname[len], "/"); strcpy(&tempdirname[len + 1], filenames[j]); dirname[dirlen++] = tempdirname; } if ((Vec & I) == I) { do_i(path); } if ((Vec & s) == s) { do_s(path); } if ((Vec & l) == 0) { if (S_ISDIR(info.st_mode)) // 判断是否为目录 { printf(GREEN "%s\t" NONE, filenames[j]); } else { printf(BLUE "%s\t" NONE, filenames[j]); } } if ((Vec & l) == l) { void mode_to_letters(); char modestr[11]; mode_to_letters(info.st_mode, modestr); printf("%s ", modestr); printf("%4d ", (int)info.st_nlink); printf("%-8s ", uid_to_name(info.st_uid)); printf("%-8s ", gid_to_name(info.st_gid)); printf("%8ld ", (long)info.st_size); printf("%.12s ", ctime(&info.st_mtime)); if (S_ISDIR(info.st_mode)) // 判断是否为目录 { printf(GREEN "%s\t" NONE, filenames[j]); } else { printf(BLUE "%s\t" NONE, filenames[j]); } printf("\n"); } if ((tag % 5 == 0) && ((Vec & l) == 0)) { printf("\n"); } } // 清空容器 for (int k = 0; k < file_cnt; k++) { memset(filenames[k], 4096, '\0'); } file_cnt = 0; } } void tags_cal(int argc, char* argv[]) { for (int i = 1; i < argc; i++) { if (argv[i][0] != '-') { // 只接受以'-'开头的参数,其它参数要么错误,要么是文件夹名称或文件名 char* tempdirname = (char*)malloc(sizeof(char) * 4096); strcpy(tempdirname, argv[i]); dirname[dirlen++] = tempdirname; } else { int len = strlen(argv[i]); for (int j = 1; j < len; j++) { switch (argv[i][j]) { case 'a': Vec |= a; break; case 'l': Vec |= l; break; case 'R': Vec |= R; break; case 't': Vec |= t; break; case 'r': Vec |= r; break; case 'i': Vec |= I; break; case 's': Vec |= s; break; default: fprintf(stderr, "%c参数错误!\n", argv[i][j]); break; } } } } if (dirlen == 0) { dirlen = 1; char* tempdirname = (char*)malloc(sizeof(char) * 2048); strcpy(tempdirname, "."); dirname[0] = tempdirname; } } void do_i(char filename[]) { struct stat info; if (stat(filename, &info) == -1) perror(filename); printf("%llu\t", info.st_ino); } void do_s(char filename[]) { struct stat info; if (stat(filename, &info) == -1) perror(filename); printf("%4llu\t", info.st_size / 4096 * 4 + (info.st_size % 4096 ? 4 : 0)); } int do_name(char dirname[]) { int i = 0; int len = 0; DIR* dir_ptr; struct dirent* direntp; if ((dir_ptr = opendir(dirname)) == NULL) { fprintf(stderr, "权限不够,cannot open: %s\n", dirname); return -1; } else { while ((direntp = readdir(dir_ptr))) { restored_ls(direntp); } sort(filenames, 0, file_cnt - 1); } printf("\n"); closedir(dir_ptr); return 1; } void sort(char** filenames, int start, int end) { if (start < end) { int position = partition(filenames, start, end); sort(filenames, start, position - 1); sort(filenames, position + 1, end); } } int partition(char** filenames, int start, int end) { if (!filenames) return -1; char* privot = filenames[start]; while (start < end) { while (start < end && compare(privot, filenames[end]) < 0) --end; swap(&filenames[start], &filenames[end]); while (start < end && compare(privot, filenames[start]) >= 0) ++start; swap(&filenames[start], &filenames[end]); } return start; } void swap(char** s1, char** s2) { char* tmp = *s1; *s1 = *s2; *s2 = tmp; } int compare(char* s1, char* s2) { if (*s1 == '.') s1++; if (*s2 == '.') s2++; while (*s1 && *s2 && *s1 == *s2) { ++s1; ++s2; if (*s1 == '.') s1++; if (*s2 == '.') s2++; } return *s1 - *s2; } void restored_ls(struct dirent* cur_item) { char* result = (char*)malloc(sizeof(char) * 4096); strcpy(result, cur_item->d_name); filenames[file_cnt++] = result; } void mode_to_letters(int mode, char str[]) { strcpy(str, "----------"); if (S_ISDIR(mode)) str[0] = 'd'; if (S_ISCHR(mode)) str[0] = 'c'; if (S_ISBLK(mode)) str[0] = 'b'; if (mode & S_IRUSR) str[1] = 'r'; if (mode & S_IWUSR) str[2] = 'w'; if (mode & S_IXUSR) str[3] = 'x'; if (mode & S_IRGRP) str[4] = 'r'; if (mode & S_IWGRP) str[5] = 'w'; if (mode & S_IXGRP) str[6] = 'x'; if (mode & S_IROTH) str[7] = 'r'; if (mode & S_IWOTH) str[8] = 'w'; if (mode & S_IXOTH) str[9] = 'x'; } char* gid_to_name(gid_t gid) { struct group *getgrgid(), *grp_ptr; static char numstr[10]; if ((grp_ptr = getgrgid(gid)) == NULL) { sprintf(numstr, "%d", gid); return numstr; } else { return grp_ptr->gr_name; } } char* uid_to_name(gid_t uid) { struct passwd* getpwuid(); struct passwd* pw_ptr; static char numstr[10]; if ((pw_ptr = getpwuid(uid)) == NULL) { sprintf(numstr, "%d", uid); return numstr; } else { return pw_ptr->pw_name; } } void do_t(char** filenames) { char temp[2048] = {0}; struct stat info1; struct stat info2; for (int i = 0; i < file_cnt - 1; i++) { for (int j = i + 1; j < file_cnt; j++) { stat(filenames[i], &info1); stat(filenames[j], &info2); if (info1.st_mtime < info2.st_mtime) { strcpy(temp, filenames[i]); strcpy(filenames[i], filenames[j]); strcpy(filenames[j], temp); } } } } void do_r(char** arr, int file_cnt) { // 只需要修改指针 char left = 0; char right = file_cnt - 1; char temp; while (left < right) { char* temp = arr[left]; arr[left] = arr[right]; arr[right] = temp; left++; right--; } }
总结
这个 myls
的难点在于整个系统的设计,比如参数怎么处理,怎么根据参数输出相应的信息。而函数的实现就比较简单。
到此这篇关于C++实现简单的ls命令及其原理的文章就介绍到这了,更多相关C++ ls命令内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!