Ls命令实现

命令行下ls算是最常用的几个命令之一,其基本功能是列出指定目录下文件的基本信息(通常是列出文件名),当然我们也可以得到文件详细的信息,比如权限、所有者、所属组、文件大小等信息。

在完成ls命令时需要用到对目录文件信息的读取、读取文件基本信息、以及基本信息转换等问题。其中前两部分的实现首先要了解系统提供给我们的系统调用以及系统在头文件中定义的变量名的意义。最后在对基本信息进行转换的过程需要我们对linux设计的机理有比较深刻的把握才能做好。

下面先附上代码实现:

#include <stdio.h>

#include <sys/types.h>

#include <dirent.h>

#include <sys/stat.h>

#include <string.h>

#include <pwd.h>

#include <grp.h>

 

void do_ls(char *dirname);

void dostat(char *filename);

void show_file_info(char *, struct stat*);

void mode_to_letters(int mode, char *str);

char *uid_to_name(uid_t uid);

char *gid_to_name(gid_t gid);

 

int main(int argc, char *argv[])

{

    if(argc == 1)

       do_ls(“.”);

    else

       while(–argc){

           printf(“%s :\n”, * ++argv);

           do_ls(*argv);

       }

    return 0;

}

 

void do_ls(char *dirname)

{

    /*

     * the struct defined in /usr/include/sys/dirent.h

     * the function defined in /usr/include/dirent.h

     *

     * #define __DARWIN_MAXNAMLEN  255

     * struct dirent

     * {

     *     ino_t d_ino;        

     *     __uint16_t d_reclen;    

     *     __uint8_t  d_type;      

     *     __uint8_t  d_namlen;    

     *     char d_name[__DARWIN_MAXNAMLEN + 1];

     * };

     *

     * DIR *opendir(const char *);

     * struct dirent *readdir(DIR *);

     * DIR *closrdir(const char *);

     */

    DIR *dir_ptr;

    struct dirent *direntp;

    if((dir_ptr = opendir(dirname)) == NULL)

       fprintf(stderr, “cannot open %s !\n”, dirname);

    else{

       // open the dir and get each file’s name

       while((direntp = readdir(dir_ptr)) != NULL)

           dostat(direntp->d_name);

       closedir(dir_ptr);

    }

}

 

void dostat(char *filename)

{

    /*

     * the struct stat & function defined in /usr/include/sys/stat.h

     *

     * struct stat {

     *        dev_t           st_dev;        

     *     ino_t           st_ino;        

     *     mode_t          st_mode;       

     *     nlink_t         st_nlink;      

     *     uid_t           st_uid;      

     *     gid_t           st_gid;        

     *     dev_t           st_rdev;      

     *  #if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)

     *     struct  timespec st_atimespec; 

     *     struct  timespec st_mtimespec; 

     *     struct  timespec st_ctimespec; 

     *  #else

     *     time_t          st_atime;      

     *     long            st_atimensec;  

     *     time_t          st_mtime;      

     *     long            st_mtimensec;  

     *     time_t          st_ctime;      

     *     long            st_ctimensec;  

     *      #endif

     *     off_t           st_size;       

     *     blkcnt_t        st_blocks;     

     *     blksize_t       st_blksize;    

     *     __uint32_t      st_flags;      

     *     __uint32_t      st_gen;        

     *     __int32_t       st_lspare;    

     *     __int64_t       st_qspare[2];  

     * };

     *

     * int stat (const char *, struct stat *);

     */

    struct stat info;

    // copy file’s info into the struct

    if(stat(filename, &info) == -1)

       perror(filename);

    else

       show_file_info(filename, &info);

}

 

void show_file_info(char *filename, struct stat *info_p)

{

    // show and modify the format

    char modestr[11];

    mode_to_letters(info_p->st_mode, modestr);

    printf(“%s “, modestr);

    printf(“%4d   “, (int)info_p->st_nlink);

    printf(“%-8s  “, uid_to_name(info_p->st_uid));

    printf(“%-8s”, gid_to_name(info_p->st_gid));

    printf(“%8ld  “, (long)info_p->st_size);

    // printf( “%.12s “, 4+ctime(&info_p->st_mtime));

    printf(“  %s\n”, filename);

}

 

void mode_to_letters(int mode, char *str)

{

    /*

     * defined in /usr/include/sys/stat.h

     * #define S_IFMT          0170000        

     * #define S_IFIFO         0010000        

     * #define S_IFCHR         0020000        

     * #define S_IFDIR         0040000        

     * #define S_IFBLK         0060000        

     * #define S_IFREG         0100000        

     * #define S_IFLNK         0120000        

     * #define S_IFSOCK        0140000        

     * #define S_IFWHT         0160000

     *

     * #define S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)    

     * #define S_ISFIFO(m)     (((m) & S_IFMT) == S_IFIFO)    

     * #define S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)    

     * #define S_ISLNK(m)      (((m) & S_IFMT) == S_IFLNK)    

     * #define S_ISSOCK(m)     (((m) & S_IFMT) == S_IFSOCK)   

     * #define S_ISWHT(m)      (((m) & S_IFMT) == S_IFWHT)           

     */

    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 *uid_to_name(uid_t uid)

{

    struct passwd *getpwuid(), *pw_ptr;

    static char numstr[10];

    if((pw_ptr = getpwuid(uid)) == NULL){

        sprintf(numstr, “%d”, uid);

       return numstr;

    }

    else

       return pw_ptr->pw_name;

}

 

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;

}

主函数依旧只是起到取参数和调用具体实现的作用,命令行下如何取得参数以及argcargv的具体意义已经在前一篇文章中提过了。在do_ls中我贴出了struct dirent的定义和接下来所涉及到的系统调用的函数原型,其中struct dirent是定义在/usr/include/sys/dirent.h这个头文件中的,它的作从readdir调用中接收目录中各个文件的文件名并将文件名传给stat函数作为参数。

系统调用stat的作用是将所传入的参数所指代文件的基本信息写入到struct stat类型的变量中,这个变量的结构体原型定义在/usr/include/sys/stat.h中,包含的信息很多,这里只列出最基本的几个文件信息。

输出信息中关于uidgid、链接数的意义将在接下来的文章中细说。这里主要就谈谈文件mode栏信息的表示和意义。

首先,我们在通过ls -l命令列出文件信息时,最开始看到的是10个字符的字符串,这十个字符可拆成4段(1-3-3-3)。第一个字符用来表示文件的类型,比如这是个目录文件,则第一个字符就是‘d’。接下来三个字符表示的是文件所有者的使用权限(三个字符分别是rwx,指代读取权限、写入权限和执行权限),如果作者有读取和写入权限而不具有执行权限的话,则这三个字符显示为‘rw-’。接下来三个字符表示的是与所有者同组的人的权限,表示方式依旧是从左到右是读、写、执行。最后三个字符表示本机其他登陆者对于该文件的使用权限。

但是struct stat结构中返回的st_mode值确定并不直接就是这种表示方式。原本表示文件基本权限信息的是一个16bits的数据,st_mode变量通过切割分段的方式将其此16bits数据分成5段,分别表示(文件类型4bits – 特殊属性3bits – 文件所有者权限3bits – 所有者同组用户权限3bits – 其他用户权限3bits),之后再将这5段分别用八进制表示出来,这种表示方式我们在IP地址点分十进制表示方式里见过。比如这16bit的数据是:1000000110110100就可以先分为1000-000-110-110-100,对应的八进制就是10664

我们需要将这16位转化成我们熟悉的表达方式,这里转化的过程和计算机网络里通过掩码得到网络号、子网号、主机号的方式相同,也是通过取与的位运算来表示,而在实现时使用了/usr/include/sys/stat.h头文件中的预定义,但实际实现的功能和我们预想的相同。最后将得到的信息逐字符存入记录结果的字符串中,显示该字符串即可完成转换。

Advertisements