大连仟亿科技
客服中心
  • 电话
  • 电话咨询:0411-39943997
  • 手机
  • 手机咨询:15840979770
    手机咨询:13889672791
网络营销 >更多
您现在的位置:仟亿科技 > 新闻中心 > 常见问题

Linux ll命令(ls -l)的代码实现

作者:billionnet 发布于:2012/12/4 17:03:04 点击量:

 

$ ll wget
total 544
drwxr-xr-x 3 orisun orisun 4096 2011-12-15 09:48 ./
drwxr-xr-x 65 orisun orisun 4096 2011-12-20 19:45 ../
drwxr-xr-x 11 orisun orisun 4096 2011-08-09 21:55 wget-1.13/
-rw-r--r-- 1 orisun orisun 540756 2011-12-15 09:47 wget.pdf

目录总是不为空,它至少包含两项:.代表当前路径,..代表父路径。

ll命令用于列出一个目录下的文件的详细信息:

  1. 文件模式
    这是个16位的2进制。最高4位表示文件类型,接下来3位依次是suid、sgid、sticky,后面的9位依次是user、group、other的读写执行标志。
    文件类型:
    1000    -    普通文件
    0100    d    目录
    0110    b    块设备
    0010    c    字符设备
    0001    p    命名管道
    1010    l    符号链接
    1100    s    套接字
    文件可读意味着可以用cat命令查看,可写可以用vi打开。文件夹可执行则可以使用cd命令。
    suid代表set-user-id,设置用户ID,即执行该文件的用户就像本文件的所有者一样。比如任何用户都可以通过password命令来修改自己的密码,但是存储密码的文件/etc/passwd的拥有者是root,其他用户是没有写权限的。而可执行文件/usr/bin/passwd的拥有者也是root,同时它的suid位上是1。则普通用户在运行/usr/bin/passwd的时候就成了root身份,既然是root身份自然对/etc/passwd拥有了写权限。那么passwd命令能不能修改其他用户的密码呢?不能!因为passwd命令会通过系统调用getuid来获得当前用户的ID,它只能修改这个ID用户的密码。如果文件拥有者本来具备写权限,同时suid位上是1,则相就位上的x会变成s;如果文件本来不是可执行文件,则相应位上会变成S。
    sgid与suid类同。
    sticky对文件和文件夹有不同的功用。sticky文件告诉系统要把文件放在swap区域。从交换空间加载程序比从普通磁盘区域加载要快,因为程序在硬盘上可能被分为好几块存放在许多不同的地方,而在交换空间上文件是不分块的。所以常用的程序如编辑器、编译器会放在交换空间。不过现在交换技术已经没那么重要了,取而代之的是虚拟内存,因为虚拟内存可以以更小的单位(如页page)进行交换。sticky使得一个目录里的文件只能被创建者删除,在/tmp目录里,谁都可以进行创建/删除文件。其他用户的x被替换这t,代表sticky被设置。
  2. 第二列显示链接数(硬链接),即指向该文件的引用次数。
  3. 第三列是文件所有者。/etc/passwd文件中包含用户列表,但它并没有包含所有的用户,因为在网络系统中,一台机器可以被很多用户登录,此时所有的用户信息都存储在一台叫作NIS的服务器上,所有的主机通过NIS进行身份验证。当然本地的/etc/passwd也存储了部分用户列表(这是NIS上用户列表的一个子集),以备离线操作。每个用户都有一个ID,该用户创建的文件有一个属性值就是此ID,这里就有一个问题,当用户被删除后,他创建的文件还在,如果后来又到了一个新用户,他刚好又被赋予了被删除的那个用户的ID,那么原先用户创建的文件归新用户所有。
  4. 第四列是文件拥有者所在的主组。不错,一个用户可以属于多个组。/etc/group里存储组列表。
  5. 第五列是文件大小。令我们好奇的是为什么上面三个文件夹的大小都是4096?这是因为目录所占的空间是以块为分配单位的,每块为512B。对于一般文件,则列出实际字节数。
  6. 第六列是文件的后面修改时间。
  7. 第七列是文件名。

下面讨论如何来编程实现ll命令。

文件的很多基本信息都可以通过系统调用lstat来获取,它返回一个结构体:

 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里面似乎包含了我们编写ll命令所需的所有文件信息,但有些表现形式上还不对。

  1. 文件模式我们希望以“-rw-rw-r--"的形式显示,但st_mode是个十进制数。这里可以使用掩码来获取相应的位标识。
  2. 用户和组提供的是ID,而不是名称。通过UID来获取用户名可以用getpwuid(uid)->p_name,根据GID来获取组名可以用getgrgid(gid)->gr_name。getpwuid()先从本地/etc/passwd上查找用户,如果没有,再从NIS中获取。
  3. st_mtime是time_t类型的,我们可以通过系统调用localtime把它转换成struct tm类型:
    struct tm {
    int tm_sec;         /* seconds */
    int tm_min;         /* minutes */
    int tm_hour;        /* hours */
    int tm_mday;        /* day of the month */
    int tm_mon;         /* month */
    int tm_year;        /* year */
    int tm_wday;        /* day of the week */
    int tm_yday;        /* day in the year */
    int tm_isdst;       /* daylight saving time */
    };
     
    需要注意的是,这里的tm_year是减去了1990的,tm_mon是从0开始计算的。

下面给出完整代码。

#include
#include
#include
#include
#include
#include
#include<string.h>
void do_ls(char[]);
void dostat(char *);
void show_file_info(char *,struct stat *);
void mode_to_letters(int,char[]);
char * uid_to_name(uid_t);
char * gid_to_name(gid_t);

void main(int argc,char *argv[]){
    if(argc==1)
        do_ls(".");
    else
        printf("输入命令./ls即可,不要带任何参数。\n");
}

void do_ls(char dirname[]){
    DIR *dir_ptr;   //路径变量
    struct dirent *direntp;     //存储路径下一个子项信息的结构体
    if((dir_ptr=opendir(dirname))==0)
        fprintf(stderr,"ls:cannot open %s\n",dirname);
    else{
        while((direntp=readdir(dir_ptr))!=0)
            dostat(direntp->d_name);
        closedir(dir_ptr);
    }
}

void dostat(char *filename){
    struct stat info;
    if(lstat(filename,&info)==-1)
        perror("lstat");
    else
        show_file_info(filename,&info);
}

void show_file_info(char *filename,struct stat *info_p){
    char modestr[11];
    mode_to_letters(info_p->st_mode,modestr);
    printf("%-12s",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);
    time_t timelong=info_p->st_mtime;
    struct tm *htime=localtime(&timelong);
    printf("%-4d-%02d-%02d %02d:%02d",htime->tm_year+1990,htime->tm_mon+1,htime->tm_mday,htime->tm_hour,htime->tm_min);
    printf(" %s\n",filename);
}

/*这个函数写得不够全面,首先文件类型不全,其次没有考虑suid,sgid,sticky*/
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';
    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';
}

#include     //#include可以出现在代码中的任何位置
char * uid_to_name(uid_t uid){
    struct passwd *pw_str;
    static char numstr[10];
    if((pw_str=getpwuid(uid))==NULL){
        sprintf(numstr,"%d",uid);       //如果没有获得用户名,则直接把uid当作用户名
        return numstr;
    }
    else
        return pw_str->pw_name;
}

#include
char * gid_to_name(gid_t gid){
    struct group *grp_ptr;
    static char numstr[10];
    if((grp_ptr=getgrgid(gid))==NULL){
        sprintf(numstr,"%d",gid);
        return numstr;
    }
    else
        return grp_ptr->gr_name;
}
 

Linux ll命令(ls -l)的代码实现

 


分享到:


评论加载中...
内容:
评论者: 验证码:
  

Copyright@ 2011-2017 版权所有:大连仟亿科技有限公司 辽ICP备11013762-1号   google网站地图   百度网站地图   网站地图

公司地址:大连市沙河口区中山路692号辰熙星海国际2215 客服电话:0411-39943997 QQ:2088827823 42286563

法律声明:未经许可,任何模仿本站模板、转载本站内容等行为者,本站保留追究其法律责任的权利! 隐私权政策声明