Linux下的 ls 命令的简单实现
又到了期末的时候,Linux实践课的老师挺厚道,布置了算是一个入门的大作业:实现一个简单版本的ls命令。三个文件,先上传再说。
ls2.c
#include <stdio.h> #include "ls_header.h" int main(int argc, char **argv) { // 处理参数, lsLong是带有 -l 参数的, lsShort为没有-l参数的ls命令 int i; if (argc == 1) { /* ls . */ lsShort("./"); return 0; } if (argc == 2 && !strcmp(argv[1], "-l") ) { /* ls -l . */ lsLong("./"); return 0; } if (argc > 2 && !strcmp(argv[1], "-l") ) { /* ls -l dirname */ for(i = 2; i < argc; ++ i) { printf("%s:\n", argv[i]); lsLong(argv[i]); if(i != argc - 1) printf("\n"); } return 0; } else { /* ls dirname */ for (i = 1; i < argc; ++ i) { printf("%s:\n", argv[i]); lsShort(argv[i]); if(i != argc - 1) printf("\n"); } return 0; } return 0; }
ls_header.h
#include <stdio.h> #include <dirent.h> // for DIR, dirent #include <string.h> #include <sys/ioctl.h> #include <unistd.h> #include "ls_util.h" // struct struct attribute file_attribute[200]; // maximum 200 void lsLong(char *dirname) { DIR *mydir = opendir( dirname ); /* directory */ char filename[20]; int file_num = 0; if (mydir == NULL) { // 显示单个文件的详细信息 strcpy(filename, dirname); ls_long_file("./", filename, &file_attribute[0]); ++ file_num; } else { // 考虑用户输入文件夹没有输入反斜杠的情况 int len = strlen(dirname); if (dirname[len - 1] != '/') { dirname[len] = '/'; dirname[len+1] = '\0'; } // 循环得到当前目录下的所有文件名,并存储在自定义的结构体中 struct dirent *mydirent; /* file */ while ( (mydirent = readdir( mydir )) != NULL) { char filename[20]; strcpy(filename, mydirent->d_name); // 不能为隐藏文件 if (!strcmp(filename, ".") || !strcmp(filename, ".") || filename[0] != '.') { ls_long_file(dirname, filename, &file_attribute[file_num ++]); } } closedir( mydir ); } // 按照文件名排序 struct attribute temp; char filename1[20]; char filename2[20]; int i, j; for (i = 0; i < file_num; ++i) { for (j = i + 1; j < file_num; ++ j) { strcpy(filename1, file_attribute[i].filename); strcpy(filename2, file_attribute[j].filename); if ( strcmp(filename1, filename2) > 0) { temp = file_attribute[i]; file_attribute[i] = file_attribute[j]; file_attribute[j] = temp; } } } // 格式化输出时,考虑每个属性值的范围 int max_mode = 0; int max_links = 0; int max_user_name = 0; int max_group_name = 0; int max_size = 0; int max_mtime = 0; int max_filename = 0; int max_extra = 0; for (i = 0; i < file_num; ++ i) { if ( max_mode < strlen(file_attribute[i].mode) ) { max_mode = strlen(file_attribute[i].mode); } if (max_links < f(file_attribute[i].links)) { max_links = f(file_attribute[i].links); } if ( max_user_name < strlen(file_attribute[i].user_name) ) { max_user_name = strlen(file_attribute[i].user_name); } if ( max_group_name < strlen(file_attribute[i].group_name) ) { max_group_name = strlen(file_attribute[i].group_name); } if (max_size < f(file_attribute[i].size)) { max_size = f(file_attribute[i].size); } if ( max_mtime < strlen(file_attribute[i].mtime) ) { max_mtime = strlen(file_attribute[i].mtime); } if ( max_filename < strlen(file_attribute[i].filename) ) { max_filename = strlen(file_attribute[i].filename); } if ( max_extra < strlen(file_attribute[i].extra) ) { max_extra = strlen(file_attribute[i].extra); } } for (i = 0; i < file_num; ++i) { char format[50]; // 定义输出的格式 sprintf(format, "%%%ds %%%dd %%%ds %%%ds %%%dld %%%ds %%s%%s\n", max_mode, max_links, max_user_name, max_group_name, max_size, max_mtime); // 按照定义的输出格式输出 printf(format, file_attribute[i].mode, file_attribute[i].links, file_attribute[i].user_name, file_attribute[i].group_name, file_attribute[i].size, file_attribute[i].mtime, file_attribute[i].filename, file_attribute[i].extra); } } // 处理不带 -l 参数的 ls 命令 void lsShort(char *dirname) { DIR *mydir = opendir( dirname ); /* directory */ // 用来暂时存储要显示的目录下的所有文件名,可以看到最大可以支持200个文件,但是每个文件名最长为20 char filenames[200][20]; int file_num = 0; if (mydir == NULL) { // 直接显示该文件 printf("%s\n\n", dirname); return ; } else { // 循环检查下面有多少文件,并把文件名全部放到filenames数组里 struct dirent *mydirent; /* file */ while ( (mydirent = readdir( mydir )) != NULL) { char fname[20]; strcpy(fname, mydirent->d_name); if (fname[0] != '.' ) { strcpy(filenames[file_num], mydirent->d_name); file_num ++; } } closedir( mydir ); } // 文件名排序 int i, j; char temp[20]; for(i = 0; i < file_num; ++ i) { for(j = i+1; j < file_num; ++ j) { if(strcmp(filenames[i], filenames[j]) > 0) { strcpy(temp, filenames[i]); strcpy(filenames[i], filenames[j]); strcpy(filenames[j], temp); } } } // 确定所有文件里面最长的文件名的长度 int max_len = 0; for(i = 0; i < file_num; ++ i) { int len = strlen(filenames[i]); if(len > max_len) { max_len = len; } } // 得到当前终端的分辨率 int cols = 80; int lines = 24; getTerminatorSize(&cols, &lines); char format[20]; sprintf(format, "%%-%ds ", max_len); // 格式化输出,当长度大于终端的列数时,换行 int current_len = 0; for(i = 0; i < file_num; ++ i) { printf(format, filenames[i]); current_len += max_len + 2; if(current_len + max_len + 2 > cols) { printf("\n"); current_len = 0; } } printf("\n"); }
ls_util.h
#include <stdio.h> #include <sys/stat.h> // 这个头文件用来得到文件的详细信息 #include <string.h> #include <time.h> // 时间头文件 #include <pwd.h> // 用来得到用户名 #include <grp.h> // 用来得到组名 // 结构体,用来存储要输出的每个属性值 struct attribute { char mode[10]; // 文件属性和权限 int links; // 链接数 char user_name[20]; // 用户名 char group_name[20]; // 所在的用户组 long size; // 文件大小 char mtime[20]; // 最后修改的时间 char filename[255]; // 文件名 char extra[3]; // 用来显示时候要加 "*"(可以执行的文件) 或者 "/" (目录) 的额外字符串 }; // 计算整数 n 有几位 int f(long n) { int ret = 0; while(n) { n = n / 10; ++ ret; } return ret; } // 得到终端的列数和行数 void getTerminatorSize(int *cols, int *lines) { #ifdef TIOCGSIZE struct ttysize ts; ioctl(STDIN_FILENO, TIOCGSIZE, &ts); *cols = ts.ts_cols; *lines = ts.ts_lines; #elif defined(TIOCGWINSZ) struct winsize ts; ioctl(STDIN_FILENO, TIOCGWINSZ, &ts); *cols = ts.ws_col; *lines = ts.ws_row; #endif /* TIOCGSIZE */ } // 由 int 型的 mode,得到实际要显示的字符串 void mode2str(int mode, char str[]) { strcpy(str, "----------\0"); if(S_ISDIR(mode)) str[0] = 'd'; if(S_ISCHR(mode)) str[0] = 'c'; if(S_ISBLK(mode)) str[0] = 'b'; if(S_ISLNK(mode)) str[0] = 'l'; 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'; } // 根据用户的 id 值,得到用户名 void uid2str(uid_t uid, char *user_name) /* 将uid转化成username */ { struct passwd *pw_ptr; pw_ptr = getpwuid(uid); if( pw_ptr == NULL) { sprintf(user_name, "%d", uid); } else { strcpy(user_name, pw_ptr->pw_name); } } // 根据用户组的 id 值,得到用户组名 void gid2str(gid_t gid, char *group_name) /* 将uid转化成username */ { struct group *grp_ptr; grp_ptr = getgrgid(gid); if( grp_ptr == NULL) { sprintf(group_name, "%d", gid); } else { strcpy(group_name, grp_ptr->gr_name); } } // 时间的格式化字符串, 注意这里我把前面的星期和后面的年份都去掉了 void time2str(time_t t, char *time_str) { strcpy( time_str, ctime(&t) + 4); time_str[12] = '\0'; } // 要显示的某一个文件详细信息,并把信息放在结构体 attribute 中 void ls_long_file(char *dirname, char *filename, struct attribute *file_attri) { // 根据文件夹名和文件名得到全名 char fullname[256]; strcpy(fullname, dirname); strcpy(fullname + strlen(dirname), filename); struct stat mystat; if ( stat(fullname, &mystat) == -1) { printf("ls_long_file: stat error\n"); } else { // 这里参考 <stat.h> 头文件 int mode = (int) mystat.st_mode; int links = (int) mystat.st_nlink; int uid = (int) mystat.st_uid; int gid = (int) mystat.st_gid; long size = (long) mystat.st_size; long mtime = (long) mystat.st_mtime; char str_mode[10]; /* 文件类型和许可权限, "drwxrwx---" */ char str_user_name[20]; char str_group_name[20]; char str_mtime[20]; // 这里就是直接调用上面的函数啦,洒家就不解释了 mode2str(mode, str_mode); uid2str(uid, str_user_name); gid2str(gid, str_group_name); time2str(mtime, str_mtime); char extra[3] = "\0\0"; if (str_mode[0] == 'd') { extra[0] = '/'; } else if (str_mode[0] == '-' && str_mode[3] == 'x') { extra[0] = '*'; } // 存储在结构体中 strcpy(file_attri->mode, str_mode); file_attri->links = links; strcpy(file_attri->user_name, str_user_name); strcpy(file_attri->group_name, str_group_name); file_attri->size = size; strcpy(file_attri->mtime, str_mtime); strcpy(file_attri->filename, filename); strcpy(file_attri->extra, extra); // 大概要得到的效果就是下面的情况啦! /* drwxr-xr-x 5 halfish halfish 4096 12月 28 10:35 Downloads/ */ } }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。