Mac下粗略实现ls命令

来源:互联网 发布:北京培训seo哪个好 编辑:程序博客网 时间:2024/05/23 13:03

  • 文件结构
  • main函数
  • 命令行解析
  • 输出结构字段
  • 输出文件列表
    • 读取目录结构
    • getFileDetails
      • 获取基本信息
      • 组装文件属性
      • 输出列表
  • Makefile的编写
  • 结果演示
  • 代码链接

在*nix系统下,ls命令都是一个很重要的命令,用于某一文件夹中的文件列表,这个命令实现起来其实也十分简单,今天闲来无事,就在Mac上粗略的写写代码。

文件结构

不关注内部细节,ls的主要实现应该包括两个部分,一部分是参数解析,一部分是输出文件列表,所以我的项目文件结构如下所示:
文件结构
main:程序入口点,调用输出列表函数
fileinfo:ls命令输出行model
parsecmd:该文件主要是参数解析部分,由于只是粗略的写写,并没有做什么检查,也只实现了ls中的-a和-l选项
dirhandler:该文件主要用于读取文件目录列表和文件相关信息。

main函数

首先来看看main函数中的代码,如下所示:

int main(int argc, const char * argv[]){    char* args = NULL;    int argsCount = ParseCmds(argc, argv, args);    int paths = argc - 1 - argsCount;    if(paths == 0)        showFilesWithArgs(".", args);    else        for(int i = argsCount + 1; i != argc; ++i)        {            if(paths > 1)                printf("%s:\n", argv[i]);            showFilesWithArgs(argv[i], args);        }    return 0;}

首先,解析参数,结束之后,如果发现用户没有指定目录,则将目录设置为当前目录,否则循环输出每个目录的信息。

命令行解析

由于只是粗略的解析一下,所以命令行解析也只是简单的将命令字符分离出来,代码如下所示:

int ParseCmds(int argc, const char* argv[], char*& args){    clean(args);    args = (char*)malloc(sizeof(char) * ARGU_LEN);    memset(args, 0, sizeof(char) * ARGU_LEN);    int cmd_index = 0, argsCount = 0;    for(int i = 1; i != argc; ++i)    {        const char* strp = *(argv + i);        if((*strp) != '-')            break ;        ++argsCount;        size_t arglen = strlen(strp);        for(int j = 1; j != arglen; ++j)            args[cmd_index++] = *(strp + j);    }    return argsCount;}

首先清空参数数组,然后为参数数组分配足够的空间,命令行都是以「-」开头,所以如果不是以该字符开头的命令字符串就不是命令选项了,而应该是目录,就是说命令已经解析完毕。否则将字符一个一个的存到args数组当中。

输出结构字段

fileinfo包含ls输出的各个字段,如下所示:

typedef struct FileInfo{    int fileType;    int numberOfLinks;    unsigned long fileSize;    int abbreviatedMonth;    int abbreviatedDayOfMonth;    int abbreviatedHour;    int abbreviatedMin;    char* ownerName;    char* groupName;    char* fileName;    char* fileProp;    FileInfo* nextFile;}FileInfo;
  • fileType:该文件的类型,比如常规文件,目录,字符设备等
  • numberOfLinks:该文件所拥有的链接的数目
  • abbreviated*:该文件的最后修改日期
  • owerName:该文件的拥有者的名字
  • groupName:该文件的拥有者所在组的名字
  • fileName:该文件的名称
  • fileProp:该文件的属性字段
  • nextFile:用于保存下一个文件入口

输出文件列表

输出文件列表分为几个步骤:

  1. 读取目录结构,获取该目录下的所有文件名
  2. 读取各项文件的详细信息
  3. 填充fileinfo,组建属性字符串
  4. 根据命令行,输出相应结果

读取目录结构

*nix中提供了目录操作的相关函数,opendir, readdir, closedir,这三个函数用于打开目录入口,读取目录列表,关闭目录入口。读取目录列表将会返回一个dirent结构,描述目录的基本信息,代码如下所示:

if(chdir(path) == -1)    {        perror("chdir error");        return ;    }    DIR* dir = opendir(path);    if(dir == NULL)    {        perror("open dir error");        return ;    }    struct dirent* _dirent = NULL;    struct FileInfo* fileHeadPtr = NULL;    while((_dirent = readdir(dir)) != NULL)    {        struct FileInfo* p = (struct FileInfo*)malloc(sizeof(struct FileInfo));        p->fileName = (char*)malloc(sizeof(char) * (_dirent->d_namlen + 1));        strcpy(p->fileName, _dirent->d_name);        p->fileType = _dirent->d_type;        if(fileHeadPtr == NULL)        {            fileHeadPtr = p;            fileHeadPtr->nextFile = NULL;        }        else        {            p->nextFile = fileHeadPtr->nextFile;            fileHeadPtr->nextFile = p;        }        if(args != NULL && strlen(args) != 0)            getFileDetails(p);    }

这段代码中主要做了4件事:
1. 读取并存储目录项信息
2. 构建目录链表
3. 保存文件名称及类型
4. 如果需要读取详细信息,则操作

getFileDetails

该函数主要用于获取目录项的详细信息,使用lstat函数来读取,该函数接收一个stat的结构,便于将详细信息存放在该结构中。关于stat结构请查看

获取基本信息

除了属性字符串需要特别组装以外,其他的信息都可以直接获取到,代码如下所示:

void getFileDetails(struct FileInfo*& info){    struct stat detail;    if(lstat(info->fileName, &detail) == -1)    {        perror("lstat error");        return ;    }    info->numberOfLinks = detail.st_nlink;    info->fileSize = detail.st_size;    struct passwd* _passwd = getpwuid(detail.st_uid);    info->ownerName = (char*)malloc(sizeof(char) * (strlen(_passwd->pw_name) + 1));    strcpy(info->ownerName, _passwd->pw_name);    struct group* _group = getgrgid(detail.st_gid);    info->groupName = (char*)malloc(sizeof(char) * (strlen(_group->gr_name) + 1));    strcpy(info->groupName, _group->gr_name);    struct tm* _tm = localtime(&detail.st_mtimespec.tv_sec);    info->abbreviatedMonth = _tm->tm_mon + 1;    info->abbreviatedDayOfMonth = _tm->tm_mday;    info->abbreviatedHour = _tm->tm_hour;    info->abbreviatedMin = _tm->tm_min;    info->fileProp = getFilePropStr(detail.st_mode);    return ;}

其中,根据stat结构中的st_uid 和 st_gid,分别使用getpwduid和getgrgid两个方法,可以分别从返回的passwd和group结构中获取到文件属主名和文件属主所在的组。根据其中的st_mtimespec.tv_sec字段,可以获取到该文件的时间相关信息。

组装文件属性

getFilePropStr接收一个mode_t类型的数据,返回一个ls命令中的格式化字符串,而mode_t在stat结构中也早已拿到,根据mode_t,我们可以获取到文件的类型,文件的属主,属组和其他人所拥有的权限,代码如下所示:

char* getFilePropStr(mode_t mode){    char* prop = (char*)malloc(sizeof(char) * BUFSIZ);    if(S_ISREG(mode))        prop[0] = '-';    else if(S_ISBLK(mode))        prop[0] = 'b';    else if(S_ISCHR(mode))        prop[0] = 'c';    else if(S_ISDIR(mode))        prop[0] = 'd';    else if(S_ISLNK(mode))        prop[0] = 'l';    else if(S_ISWHT(mode))        prop[0] = 'w';    else if(S_ISFIFO(mode))        prop[0] = 'p';    else if(S_ISSOCK(mode))        prop[0] = 's';    prop[1] = ((mode & S_IRUSR) == 0 ? '-' : 'r');    prop[2] = ((mode & S_IWUSR) == 0 ? '-' : 'w');    prop[3] = ((mode & S_IXUSR) == 0 ? '-' : 'x');    prop[4] = ((mode & S_IRGRP) == 0 ? '-' : 'r');    prop[5] = ((mode & S_IWGRP) == 0 ? '-' : 'w');    prop[6] = ((mode & S_IXGRP) == 0 ? '-' : 'x');    prop[7] = ((mode & S_IROTH) == 0 ? '-' : 'r');    prop[8] = ((mode & S_IWOTH) == 0 ? '-' : 'w');    prop[9] = ((mode & S_IXOTH) == 0 ? '-' : 'x');    return prop;}

输出列表

根据命令行,决定是短输出还是长输出,如代码所示:

void printLong(struct FileInfo* info){    printf("%s %d %s %s %lu %d %d %d:%d %s\n", info->fileProp, info->numberOfLinks,           info->ownerName, info->groupName, info->fileSize, info->abbreviatedMonth,           info->abbreviatedDayOfMonth, info->abbreviatedHour, info->abbreviatedMin, info->fileName);    return ;}void printShort(char* fileName){    int printIndex = 0;    bool bNeedTab = false;    ++printIndex;    printf(bNeedTab ? "    %s" : "%s", fileName), bNeedTab = true;    if(printIndex % LS_ITEMS_PER_LINE == 0)        printf("\n"), bNeedTab = false;    return ;}

至此,整个ls命令就编写完毕。

Makefile的编写

如果要在命令行下编译,我们知道,用makefile是相当不错的选择,虽然 只是寥寥几个文件,但一个make命令即能得到结果,也是不错的。其内容如下所示:

myls: main.cpp dirhandler.o parsecmd.o fileinfo.h    g++ main.cpp dirhandler.o parsecmd.o -o mylsdirhandler.o: dirhandler.h dirhandler.cpp fileinfo.h    g++ -c dirhandler.cpp fileinfo.hparsecmd.o: parsecmd.h parsecmd.cpp    g++ -c parsecmd.cppclean:    rm dirhandler.o parsecmd.o fileinfo.h.gch

这样一来,只要在命令行中输入make,即可得到我们所需要的myls命令,输出make clean即可以清理在make过程中产生的杂碎文件。

结果演示

演示结果如图所示:

结果演示

额,似乎在最后做修改的时候,短输出的格式有点问题了这就尴尬了
不过,不要在意这些细节,那都是浮云。重点还是命令的实现过程,虽然十分简单,但做为练练手的项目,打发打发时间还是相当不错的。

代码链接

github下载

0 0
原创粉丝点击