目录与文件属性(ls的编写)

目录与文件属性(编写ls)

一、背景

ls可以说是linux命令中最常用的了,它可以显示一个目录中的文件内容。并且ls有许许多多的纷繁复杂的选项。其中-a-l-d是经常会见到的。-a选项可以来查看目录当中的隐藏文件,而-l可以用来查看文件的详细信息,再加上个-d可以查看该目录的详细信息。这个如此之常用的命令是如何实现的呢,下面就“分门别类”的一步一步来分析一下。

二、分类叙述实现过程

1.ls的实现

    要想知道这个ls是如何实现的,那么首先来看看它“干”了什么,这个就很显而易见了,ls不过是将目录中的文件列出来了而已。

   
知道了这个就可以进行联机搜索,看看什么系统调用可以实现将文件夹打开并且读出来文件夹中文件名称这一功能。

我是如此来检索这个系统调用的。最使我满意的很显然是readdir了(读一个文件夹),根据以往经验,要读文件夹你肯定得首先打开它。所以我自然联想到了有可能是用opendir来打开文件夹,closedir来关闭使用完毕的文件夹。当然,这只是猜测,实际还是来打开readdir来查看一下相关的系统调用吧。

是这个样子的,再说,其它的系统调用咋看都不像是我需要用的。接着网上翻一翻看看 readdir 该如何使用。
简直是大段大段的介绍,简而言之呢,这个 readdir 是要从文件夹中读出来一个叫做 dirent 的结构体,而这个结构体中包含了我需要得到的一个东西叫做 filename 也就是文件名了。这段介绍太过复杂,删繁就简看下 readdir 的使用,其实就是这个东西。

给它一个指向文件夹的指针,它会还你一个结构名叫做dirent的指针,换句话说,这个指针所指向的这个结构中包含了你想要的东西。

当然千万不可忘掉头文件dirent.h,否则系统都不知道你在说些什么。

同样的道理去man一下opendirclosedir的使用方法。这样下来编出了一个超简易版的ls

#include<stdio.h>

#include<dirent.h>

 

void ls_do(char dirname[]);

 

int main(int ac,char *av[])

{

        if(ac==1)

          ls_do(".");

        else

          while(--ac){

                ls_do(*++av);

                }

return 0;

}

 

void ls_do(char dirname[])

{

        DIR *dir_p;

        struct dirent *direct_p;

 

        if((dir_p=opendir(dirname))==NULL)

          fprintf(stderr,"cannot open the file %s\n",dirname);

        else

          while ((direct_p=(readdir(dir_p)))!=NULL)

          printf("%s  ",direct_p->d_name);

        printf("\n");

        close(dir_p);

 

}

 

输出的结果很是难看,不过大致有了ls的功能,当然,除过一个bug,就是系统的ls是不会输出隐藏文件的(即以“.”开头的文件),只有ls加上-a参数才会全部输出。

于是加了一些条件就行了。

 

#include<stdio.h>

#include<dirent.h>

 

void ls_do(char dirname[]);

void ls_doo(char dname[]);

struct dirent *buf;

 

int main(int ac,char *av[])

{

        if(ac==1)

          ls_do(".");

        else

        if(av[1][0]!='-'||av[1][1]!='a')

          while(--ac){

        ls_do(*++av);

        }

        else

        if(ac==2)

          ls_doo(".") ;

        else

        {

        --ac;

        ++av;

        while(--ac){

        ls_doo(*++av);

        }

        }

return 0;

}

 

void ls_do(char dirname[])

{

        DIR *dir;

        if((dir=opendir(dirname))==NULL)

        perror(dirname);

        else

        while((buf=readdir(dir))!=NULL)

          if(buf->d_name[0]!='.')

          printf("%s  ",buf->d_name);

          printf("\n");

        closedir(dir);

}

 

void ls_doo(char dname[])

{

        DIR *dir;

        if((dir=opendir(dname))==NULL)

        perror(dname);

        else

        while((buf=readdir(dir))!=NULL)

          printf("%s  ",buf->d_name);

          printf("\n");

        closedir(dir);

}

 

不过这段代码看起来真是繁琐,可已经有了选项的功能了。

这个是结果的对比。可以看的出来这个功能是有了。

 

2.ls -l的实现

首先还是看一下它“干”了什么。

可以看的出来,ls -l好像还做了不少事情。它不仅仅输出了目录中各个文件的名称,它还输出了各个文件的详细信息。第一列:文件的格式与权限。第二列:链接数。第三列:所有者名称。第四列:所属组名称。第五列:文件大小。第六列:文件最后修改时间。最后列:文件名称。

同样使用联机帮助来查找可以用的系统调用。这里应该是需要文件的属性。Information?status?property?nature?总之这些个都是有可能的。

 

总之最后找到了stat这个系统调用。所以说联机帮助好用是真的,但是信息难找也是真的。接下来当然是man一下stat了。

这里可以明白过来stat的使用方法,也就是把文件名地址给它,它会把状态信息的地址给一个结构为stat的结构体的。接着往下看就可以详细了解这个结构体的模样。

这里想要的文件属性应有尽有。

结合上一小节获得目录中的文件名就可以编出来实现ls -l的程序了。

#include<stdio.h>

#include<sys/stat.h>

#include<dirent.h>

 

 

void ll_do(char filename[]);

void ls_do(char dirname[]);

 

int main(int ac,char *av[])

{

        if(ac==1)

          ls_do(".");

        else{

          while(--ac)

          ls_do(*++av);

        }

return 0;

}

 

void ls_do(char dirname[])

{

        DIR *dir_ptr;

        struct dirent *buf;

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

          perror(dirname);

        else

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

            ll_do(buf->d_name);

        closedir(dir_ptr);

}

 

 void ll_do(char filename[])

{

        struct stat buf;

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

        perror(filename);

        else

        {

          printf("%o  ",buf.st_mode);

          printf("%d  ",buf.st_nlink);

          printf("%d  ",buf.st_uid);

          printf("%d  ",buf.st_gid);

          printf("%d  ",buf.st_size);

          printf("%d  ",buf.st_mtime);

          printf("%s\n",filename);

 

        }

}

 

运行结果如下。

这个和系统中的命令运行出的结果完全是两个样子啊。  

   

这个是系统运行出来的样子。不过经过对比可以发现,其实本质是表达出来同样的意思了。只不过形式上是难看了点。接下来的工作便是一列一列的对其进行修正,让它变得好看一些。

1)第一列

对比一下第一列不难发现问题所在。我的程序的文件格式与权限是用数字来表示的。而标准的是用字母来表示的。要进行如此转换需要用到掩码的概念。为什么要用掩码的概念呢?因为我需要看的只是其中的某一位,该位为 1 就是如何,该位为 0 就不是如何。具体来说,权限,我只需看其中的一位是否为 1 就可知道它的某一用户( user/group/other )是否具有读写或执行的权限。简单点来讲是把这些八进制的数作为二进制与另一串二进制位相与,将不需要看的位置零,如此可用 if 语句来判断之后就更改下字符串的字母就完了。
这个在头文件 sys/stat.h 中有完整的定义,而且名字也是一看就 get 了,一目了然。

其它还有很多,我只复制了其中一隅。用S_IRUSRmode相与并判断是否为非零,当然即是是否为1,如果是1,那么就将所有者的读权限更改为“r”就好了,如果不是,那就让字符串继续保持“-”。

2)第二列

无需更改

3)第三列

我的 0 代表的是 root 用户。而这个数字其实就是用户的 ID 。将 USERID 转换为名称,这就很容易让人想到 passwd 文件,这文件其中就包含了 USERID 和对应的名称。这里需要用到一个叫做 getpwuid 的系统调用。 man 一下它,从中可以看到如下这样的用法。
显然,使用这个系统调用的方法是,输出一个 uid ,它会还给你一个名字叫做 passwd 的结构体的指针。同样的套路,往下翻看结构体是什么面目。

东西不少,但我们用的就仅仅是username。到时将结构中的pw_name拿出就是。

4)第四列

和第三列处理方法类似,这里不再赘述。需要用的是group文件。对应的系统调用时getgrgid。对应的结构体名字叫做group

5)第五列

无需更改

6)第六列

时间格式这个系统调用ctime我先前用过了,这里也不赘述。就是将数字改为好看的字符的事。

7)第七列

无需更改

 

这样子下来,七列全部改完。完整代码如下:

#include<grp.h>

#include<pwd.h>

#include<time.h>

#include<stdio.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<dirent.h>

#include<string.h>

 

 

void ll_do(char filename[]);

void show_username(uid_t uid);

void ls_do(char dirname[]);

void show_time(long);

void show_groupname(int gid);

void show_mode(int mode);

 

int main(int ac,char *av[])

{

        if(ac==1)

          ls_do(".");

        else{

          while(--ac)

          ls_do(*++av);

        }

return 0;

}

 

void ls_do(char dirname[])

{

        DIR *dir_ptr;

        struct dirent *buf;

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

          perror(dirname);

        else

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

            ll_do(buf->d_name);

        closedir(dir_ptr);

}

 

 void ll_do(char filename[])

{

        struct stat buf;

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

        perror(filename);

        else

        {

        show_mode(buf.st_mode);

        printf("%d  ",buf.st_nlink);

        show_username(buf.st_uid);

        show_groupname(buf.st_gid);

        printf("%d  ",buf.st_size);

        show_time(buf.st_mtime);

        printf("%s\n",filename);

        }

}

 

void show_time(long timeval)

{

        char *cp;

        cp=ctime(&timeval);

        printf("%12.12s  ",cp+4);

}

 

void show_mode(int mode)

{

        char str[10];

        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';

        printf("%s  ",str);

}

 

void show_username(uid_t uid)

{

        struct passwd *buf;

        buf=getpwuid(uid);

        printf("%s  ",buf->pw_name);

}

 

void show_groupname(int  gid)

{

        struct group *buf;

        buf=getgrgid(gid);

        printf("%s  ",buf->gr_name);

}

  运行结果如下:

跟标准的还是挺像的。不过还是有一些问题:1.这里没把隐藏文件去掉。得用if语句去掉,这里就不赘述了。2.这个命令不可以指定文件夹。这个就是个大缺憾了,问题正在修补中。

 

3.ls -l -d的实现

    这个是要比ls -l简单些的,只是将上一节中的输入目录文件的名称改为输入目录的名称。而且这个结果比较理想,没有上一节的那两个问题。

三、总结

    这个命令介绍的比较详细,主要是其中涉及了很多没有用过的系统调用。这个系统的编写让我感触很深的是结构体的使用,结构体到处都是无处不在,而读信息用信息都是从结构体中来的。并且大多用的是指针。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值