您的位置:首页 > 其它

编写自己的ls命令

2014-05-09 14:32 351 查看
····要编写ls命令,首先要了解它能做什么,完成了什么工作,是如何完成这些工作的····

一、ls命令能做什么?

  我们在命令行输入ls,ls默认找出当前目录中所有文件的文件名,并且按照字典序排序后输出。ls命令有很多参数选项,可以决定ls的输出内容。如果参数是目录,ls列出目录的内容,如果参数是文件,ls列出文件名和属性。
  例如:ls -l
     (每行7个字段,mode+links+owers+group+size+last-modified+name)

  问题在于:

    1.如何列出目录的内容。

    2.如何读取并显示文件的属性。

    3.给出一个名字,如何判断出它是目录还是文件。

二、ls命令是如何工作的?

  文件和目录被组成一棵目录树结构。目录是一种特殊的文件,其内容是文件和目录的名字。目录是记录的序列,每条记录对应一个文件或子目录,通过readdir来读取目录中的记录,readdir返回一个指向目录的当前记录的指针,记录的类型是struct_dirent。

三、如何编写ls命令?

  框架:
    mian()
    opendir
    while(readdir)
      print d_name
    closedir

几个结构体的解析:

1、结构体__dirstream:

struct __dirstream{
         void* __fd;
         char* __data;
         int __entry_data;
         char* __ptr;
         int __entry_ptr;
         size_t __allocation;
         size_t __size;
         __libc_lock_define(,__lock)
};
typedef struct __dirstream DIR;


  DIR结构体类似于FILE,是一个内部结构,以下几个函数用这个内部结构保存当前正在被读取的目录的有关信息。
  Eg: DIR *opendir(const char *parhname);打开文件目录,返回指向DIR 结构体的指针。
     struct dirent *readdir(DIR *dp);
     void rewinddir(DIR *dp);
     int closedir(DIR *dp);
     long telldir(DIR *dp);
     void seekdir(DIR *dp,long loc);

2、结构体 dirent:

struct dirent {
      ino_t d_ino; /* inode number 索引节点号*/
      off_t d_off; /* offset to the next dirent 在目录文件中的偏移*/
      unsigned short d_reclen; /* length of this record 文件名长*/
      unsigned char d_type; /* type of file; not supported by all file system types文件类型 */
      char d_name[256]; /* filename 文件名,最长255字符*/
};


  目录文件(directory file):这种文件包含了其他文件的名字以及指向与这些文件有关的信息的指针。从此可以看出,dirent不仅仅指向目录,还指向目录中的具体文件。(readdir函数同样也可以读取目录下的文件,这就是证据)
  从上述的定义我们可以看出dirent结构体中存储的有关文件的信息太少,不能满足我们的需求,即dirent只是起到了索引的作用。所以我们要想获得ls -l类似的信息,必须通过stat函数获取。
通过readdir函数读取到文件名并存储到结构体dirent的d_name成员中,然后通过int stat(const *file_name,struct stat *buf)获取文件名为d_name的文件的详细信息,并存储在stat结构体中。

3、结构体 stat:

struct stat {
     dev_t st_dev; /* ID of device containing file 文件使用的设备号*/
     ino_t st_ino; /* inode number 索引节点号*/
     mode_t st_mode; /* protection 文件访问权限*/
     nlink_t st_nlink; /* number of hard links 文件的硬链接数*/
     uid_t st_uid; /* user ID of owner 所有者用户识别号*/
     gid_t st_gid; /* group ID of owner 组识别号*/
     dev_t st_rdev; /* device ID (if special file) 设备文件的设备号*/
     off_t st_size; /* total size, in bytes 以字节为单位的文件容量*/
     blksize_t st_blksize; /* blocksize for file system I/O 包含该文件的磁盘块的大小*/
     blkcnt_t st_blocks; /* number of 512B blocks allocated 该文件所占的磁盘块*/
     time_t st_atime; /* time of last access 最后一次访问该文件的时间*/
     time_t st_mtime; /* time of last modification 最后一次修改该文件的时间*/
     time_t st_ctime; /* time of last status change 最后一次改变文件状态的时间*/
};


 struct stat中的st_mode值各个位代表的含义:

The following flags are defined for the st_mode field:
      /*  是什么类型的文件  */
S_IFMT     0170000   bit mask for the file type bit fields
S_IFSOCK   0140000   socket
S_IFLNK    0120000   symbolic link
S_IFREG    0100000   regular file
S_IFBLK    0060000   block device
S_IFDIR    0040000   directory
S_IFCHR    0020000   character device
S_IFIFO    0010000   FIFO
S_ISUID    0004000   set UID bit
S_ISGID    0002000   set-group-ID bit (see below)
S_ISVTX    0001000   sticky bit (see below)
      /*  是否有可读写权限  */
S_IRWXU    00700     mask for file owner permissions
S_IRUSR    00400     owner has read permission
S_IWUSR    00200     owner has write permission
S_IXUSR    00100     owner has execute permission
S_IRWXG    00070     mask for group permissions
S_IRGRP    00040     group has read permission
S_IWGRP    00020     group has write permission
S_IXGRP    00010     group has execute permission
S_IRWXO    00007     mask for permissions for others (not in group)
S_IROTH    00004     others have read permission
S_IWOTH    00002     others have write permission
S_IXOTH    00001     others have execute permission


4、结构体passwd :

/* The passwd structure.  */
struct passwd
{
char *pw_name;          /* Username.  */
char *pw_passwd;        /* Password.  */
__uid_t pw_uid;         /* User ID.  */
__gid_t pw_gid;         /* Group ID.  */
char *pw_gecos;         /* Real name.  */
char *pw_dir;           /* Home directory.  */
char *pw_shell;         /* Shell program.  */
};


5、结构体group:

/* The group structure.     */
struct group
{
char *gr_name;         /* Group name.    */
char *gr_passwd;       /* Password.    */
__gid_t gr_gid;        /* Group ID.    */
char **gr_mem;         /* Member list.    */
};


6、结构体tm:

/* Used by other time functions.  */
struct tm
{
int tm_sec;            /* Seconds.    [0-60] (1 leap second) */
int tm_min;            /* Minutes.    [0-59] */
int tm_hour;            /* Hours.    [0-23] */
int tm_mday;            /* Day.        [1-31] */
int tm_mon;            /* Month.    [0-11] */
int tm_year;            /* Year    - 1900.  */
int tm_wday;            /* Day of week.    [0-6] */
int tm_yday;            /* Days in year.[0-365]    */
int tm_isdst;            /* DST.        [-1/0/1]*/

#ifdef    __USE_BSD
long int tm_gmtoff;        /* Seconds east of UTC.  */
__const char *tm_zone;    /* Timezone abbreviation.  */
#else
long int __tm_gmtoff;        /* Seconds east of UTC.  */
__const char *__tm_zone;    /* Timezone abbreviation.  */
#endif
};


sunmmary:

如果想获取某目录下(eg:目录a)b文件的详细信息,我们该怎么做?
  1、使用opendir函数打开目录a,返回指向目录a的DIR的结构体c。
  2、调用readdir(c)函数读取目录a下所有的文件(包括目录),返回指向目录a下所有文件的dirent结构体d。
  3、遍历d,调用stat(d->name,stat *e)函数来获取每个文件的详细信息,并存储在stat结构体e中。

知识点解释:

1、stat得到文件属性:
    int result = stat(char * fname , struct stat * bufp)
    包含在# include <sys/stat.h>,把文件fname的信息复制到指针bufp所指的结构中。

2、将mode模式字段转换成字符:
    st_mode是一个16位的二进制数,文件类型和权限被编码在这个数中。
    字段编码——整数是bit组成的序列,使用八进制简化表示。
    解码——掩码技术(与0做位于)。

3、将用户/组ID转换成字符串:
    etc/passwd文件中包含用户列表(搜索文件繁琐),而且其并没有包含所有的用户(网络计算系统,所有主机通过NIS进行用户身份验证)。
    通过getpwuid来得到完整的用户列表——通过库函数getpwuid来访问用户信息,如果用户信息保存在/etc/passwd中,那么getpwuid就会查找/etc/passwd的内容,如果用户信息在NIS中,getpwuid会从NIS中获取信息。
    getpwuid需要uid作为参数,返回一个指向struct passwd的指针。
    return getpwuid(uid)->pw_name
同理:/etc/group是组列表,通过getgrgid访问组列表。

4、用ctime将time_t类型的修改时间转换成字符串格式:
    char *ctime(const time_t *time);包含于time.h中

5、参数(目录OR某个文件)判断:
    if((st.st_mode & S_IFMT) == S_IFDIR)成立则是目录。

6、排序后输出:
    qsort()函数使用快速排序的例程进行排序,包含于stdlib.h中。
    void qsort(void *base,int nelem,int width,int (*fcmp)(const void *,const void *));
    参数解析: *base:待排序数组首地址
          nelem:数组中待排序元素数目
          width:各元素占用的空间大小
          *:指向函数的指针,用于确定排序的顺存

qsort参考链接:http://baike.baidu.com/view/982231.htm?fr=aladdin

7、高亮显示:

文件按照属性显示颜色
033[mode;foreground;backgroundmhello\033[0m

mode为显示模式:
0、1、22、4、24、5、25、7、27,分别表示设定颜色、黑体、非黑体、下画线、非下画线、闪烁、非闪烁、翻转、非翻转。

foreground为前景颜色:
30 (黑色)、31 (红色)、32 (绿色)、33 (黄色)、34 (蓝色)、35 ( 紫红色)、36 (青色)和37 (白色)

background为背景颜色:
40 (黑色)、41 (红色)、42 (绿色)、43 (黄色)、44 (蓝色)、45 ( 紫红色)、46 (青色)和47 (白色)

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#include <grp.h>
#include <pwd.h>
#include <time.h>

int display_file(char *path, char *filename)
{
struct stat st;
int i;
struct passwd *pw;
struct group *gr;
struct tm *tm;
stat(path, &st);

switch(st.st_mode & S_IFMT)
{
case S_IFREG:  printf("-");    break;
case S_IFDIR:  printf("d");    break;
case S_IFLNK:  printf("l");    break;
case S_IFBLK:  printf("b");    break;
case S_IFCHR:  printf("c");    break;
case S_IFIFO:  printf("p");    break;
case S_IFSOCK: printf("s");    break;
}

for(i = 8; i >= 0; i--)
{
if(st.st_mode & (1 << i))
{
switch(i%3)
{
case 2: printf("r"); break;
case 1: printf("w"); break;
case 0: printf("x"); break;
}
}
else
printf("-");
}

pw = getpwuid(st.st_uid);
gr = getgrgid(st.st_gid);

printf("%2d %s %s %4ld", st.st_nlink, pw->pw_name, gr->gr_name, st.st_size);

tm = localtime(&st.st_ctime);
printf(" %04d-%02d-%02d %02d:%02d",tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min);

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

return 0;
}

int display_dir(char *dirname)
{
DIR *dir;
struct dirent *dirent;
struct stat st;
char buf[1024];

dir = opendir(dirname);
while((dirent = readdir(dir)) != NULL)
{
strcpy(buf, dirname);
strcat(buf, "/");
strcat(buf, dirent->d_name);
if(stat(buf, &st))
{
perror("stat");
return -1;
}

if(dirent->d_name[0] != '.')
display_file(buf, dirent->d_name);
}
}

int main(int argc, char **argv)
{
struct stat st;
char buf[1024];

if(stat(argv[1], &st) < 0)
{
perror("stat");
return -1;
}

if((st.st_mode & S_IFMT) == S_IFDIR)
display_dir(argv[1]);
else
display_file(argv[1], argv[1]);

return 0;
}


童鞋代码
链接:

ubuntu 编写自己的ls命令/article/6004285.html

高亮显示:http://www.embedu.org/Column/Column341.htm
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: