Chinaunix首页 | 论坛 | 博客
  • 博客访问: 566846
  • 博文数量: 109
  • 博客积分: 2300
  • 博客等级: 大尉
  • 技术积分: 810
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-02 13:11
文章分类

全部博文(109)

文章存档

2012年(1)

2011年(17)

2010年(62)

2009年(29)

我的朋友

分类: LINUX

2009-10-17 19:44:02

1. 第一个版本还有哪些问题
  第一个版本中我们已经实现了ls显示目录下所有文件文件名的功能,但仅限如此。而实际的ls有很多参数选择。我们也不可能全部实现,现在就选一个比较常用的-l参数来实现。
1) 显示文件的详细信息,即加-l选项,效果如下:


ls -l.jpg



2. 思考怎么样解决
问题的解决方法:我们知道系统的ls命令都会打印出下面几条信息,模式、链接数、文件所有者、组、大小、最后修改时间、文件名。而这些信息都保存在文件的i结点上。我们只需要读出来分析.
前面已经说明。我们打印出一个目录下的所有文件,是读取目录结点的信息。但上面两个问题中的解决方法都要用到文件的属性。所以我们在取得文件名后还要想办法取得这个文件的属性。Linux下可以用系统调用stat来得到文件信息。我们可以通过命令 man 2 stat关于stat的说明如下
头文件
#include
#include
定义函数  int stat(const char * file_name,struct stat *buf);
函数说明  stat()用来将参数file_name所指的文件状态,复制到参数buf所指的结构中。
下面是struct stat内各参数的说明

代码:

struct stat
{
dev_t st_dev; /*文件的设备编号*/
ino_t st_ino; /*文件的i-node*/
mode_t st_mode; /*文件的类型和存取的权限*/
nlink_t st_nlink; /*连到该文件的硬连接数目,刚建立的文件值为1*/
uid_t st_uid; /*文件所有者的用户识别码*/
gid_t st_gid; /*文件所有者的组识别码*/
dev_t st_rdev; /*若此文件为装置设备文件,则为其设备编号 */
off_t st_size; /*文件大小,以字节计算*/
unsigned long st_blksize; /*文件系统的I/O 缓冲区大小 */
unsigned long st_blocks; /*占用文件区块的个数,每一区块大小为512 个字节*/
time_t st_atime; /* 文件最近一次被存取或被执行的时间,一般只有在用mknod、utime、read、write与tructate时改变*/
time_t st_mtime; /* 文件最后一次被修改的时间,一般只有在用mknod、utime和write时才会改变*/
time_t st_ctime; /* i-node最近一次被更改的时间,此参数会在文件所有者、组、权限被更改时更新先前所描述的*/
};

我们可以发现。我们上面想要知道的属性都在这个结构里了。其中如链接数、文件大小是可以直接打印、最后修改时间等可以用ctime转化成字符串;唯一的问题是有些属性我们需要做一些转换。比如模式字段现在保存的是16位的二进制数。我们需要转换成-rw-rw-r—这一类字符串;还有st_uid 保存的是用户的ID。我们需要转换成用户名,组ID st_gid也一样。好了。我们又知道接下来需要做哪些工作了。开如吧
首先将模式字段转换成字符
St_mode是一个16位的二进制数。文件类型和权限被编码在这个数中。如图5-1所示:


图5-1.gif

其中前4位用作文件类型。最多可以标识16种类型,目前已经使用了其中的7个。接下来的3位是文件的特殊属性,1代表具有某个属性。0代表没有。分别是set-user-ID位.set-group-ID位和sticky位,它们的含义后面再介绍。最的9位是许可权限,分为三组。分别是文件所有都,同组用户和其它用户。其他用户指不在同一组的用户。每组三位,分别是读、写和执行的权限,对应的地方如果是1表示该用户拥有对应权限。0表示没有。
了解这些后我们就可以考虑怎么分析了。在中已经有系列宏可以帮助我们,这些宏里使用了掩码来来解码得到文件类型,至于具体做法和原理这里就不详细介绍了。宏的使用如下:
If(S_ISDIR(info.st_mode))
Printf(“this is a directory”);
许可权限的解码也类似:我们知道最低的9位是许可权限。也已经实现相应的宏。我们现在可以将这些位一志转换为短横和字母的串,如下:

代码:

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';    /* 3位所有者权限  */
    if ( mode & S_IWUSR ) str[2] = 'w';
    if ( mode & S_IXUSR ) str[3] = 'x';
    if ( mode & S_IRGRP ) str[4] = 'r';    /* 3位同组用户权限 */
    if ( mode & S_IWGRP ) str[5] = 'w';
    if ( mode & S_IXGRP ) str[6] = 'x';
    if ( mode & S_IROTH ) str[7] = 'r';    /* 3位其它用户权限 */
    if ( mode & S_IWOTH ) str[8] = 'w';
    if ( mode & S_IXOTH ) str[9] = 'x';
}

将用户/组ID转换成字符串
接下来唯一的问题就是将用户/组ID转换成字符串,linux下用户列表文件一般在/etc/passwd中。我们可以先打开这个文件了解一下,如图5-2


图5-2.jpg

每一行代表一个用户,内容用冒号:分成不同的字段。每个字段表示的意思如下:
用户名:密码:用户ID:组:用户全名:主目录:用户使用SHELL程序的路径
这里需要注意的是:并不是所有用户都包括在/etc/passwd文件中.有些网络计算系统中会把用户信息保存到一台别的主机上。称为NIS。所有主机通过NIS来进行用户身份验证。
那么我们究竟怎么样来通过用户ID转换成用户名呢?答案是答案是库函数getpwuid,不管用户信息是保存在/etc/passwd或者是NIS中。Getpwuid都能取到用户的信息 通过man 3 getpwuid 可以获得它的帮助手册:
头文件  
#include
#include
定义函数  struct passwd * getpwuid(uid_t uid);
函数说明  getpwuid()用来逐一搜索参数uid 指定的用户识别码,找到时便将该用户的数据以结构返回结构请参考将该用户的数据以passwd 结构返回。
返回值  返回passwd 结构数据,如果返回NULL 则表示已无数据,或者有错误发生。
passwd 结构定义如下

代码:

struct passwd{
char * pw_name; /*用户账号*/
char * pw_passwd; /*用户密码*/
uid_t pw_uid; /*用户识别码*/
gid_t pw_gid; /*组识别码*/
char * pw_gecos; /*用户全名*/
char * pw_dir; /*家目录*/
char * pw_shell; /* 所使用的shell路径*/
};
所以通过uid可以方便的得到用户名:
char *uid_to_name( uid_t uid )
/*
 *  returns pointer to username associated with uid, uses getpw()
 */
{
    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 ;
}

将组ID转换成字符串
一般组列表文件是/etc/group,其内容和意义如下:
group_name:passwd:GID:user_list
在网络计算系统中。组信息也被保存在NIS中。同获取用户名一样。我们可以用类似的库函数getgrgid。同样用man 3 getgrgid获取帮助手册:
头文件  
#include#include
定义函数  strcut group * getgrgid(gid_t gid);
函数说明  getgrgid()用来依参数gid指定的组识别码逐一搜索组文件,找到时便将该组的数据以group结构返回。
返回值  返回group结构数据,如果返回NULL则表示已无数据,或有错误发生。

代码:

struct group{
char *gr_name; /*组名称*/
char *gr_passwd; /* 组密码*/
gid_t gr_gid; /*组识别码*/
char **gr_mem; /*组成员账号*/
}
所以我们可以这样写代码:
#include   
char *gid_to_name( gid_t gid )
/*
 *  returns pointer to group number gid. used getgrgid(3)
 */
{
    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;
}

3. 实现
上面我们已经知道怎么把文件的属性转换成字符串了。现在就实现把他们组合起来。实现ls –l的功能:
因为代码比较长就以文件传上。有兴趣的可以下载
点击此处下载 (文件大小:3K)

效果如下图:


ls2_3.jpg


和系统的ls -l相比。首先是少了第一行:total 其实这是统计目录下的文件总数,
另外显示出了.和..这两条。这是因为这两者默认是隐藏的。必须得加上-a参数才会显示出来。
有兴趣的朋友可以自己把以上两者尝试改正。
阅读(1805) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~