unix/linux ls命令的实现

来源:互联网 发布:慢慢买数据怎么看 编辑:程序博客网 时间:2024/05/29 19:22

Understanding Unix/Linux Programming(Unix/Linux编程实践教程)

学习模式:

(1)它能做什么?

(2)它是如何实现的?

(3)能不能自己编写一个?

(实验环境:gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) )

                                      ls命令学习

(1)ls命令能做什么

查看联机帮助:ls


可知ls默认列出当前目录的文件夹跟文件。按字典序排序。

ls还可以跟其他参数:ls -l

cong@ubantu:/$ ls -ltotal 100drwxr-xr-x   2 root root  4096 Mar  8 03:48 bindrwxr-xr-x   3 root root  4096 Mar 10 02:39 bootdrwxrwxr-x   2 root root  4096 Aug 22  2016 cdromdrwxr-xr-x  19 root root  4320 Mar 10 22:09 devdrwxr-xr-x 132 root root 12288 Mar 10 22:09 etcdrwxr-xr-x   3 root root  4096 Aug 22  2016 homelrwxrwxrwx   1 root root    32 Mar  8 03:56 initrd.img -> boot/initrd.img-4.4.0-66-genericlrwxrwxrwx   1 root root    32 Oct 24 23:51 initrd.img.old -> boot/initrd.img-4.4.0-45-genericdrwxr-xr-x  22 root root  4096 Aug 22  2016 libdrwxr-xr-x   2 root root  4096 Jul 19  2016 lib64drwx------   2 root root 16384 Aug 22  2016 lost+founddrwxr-xr-x   4 root root  4096 Aug 22  2016 mediadrwxr-xr-x   3 root root  4096 Oct 15 04:39 mntdrwxr-xr-x   2 root root  4096 Jul 19  2016 optdr-xr-xr-x 223 root root     0 Mar 10 22:08 procdrwx------   4 root root  4096 Oct 14 09:10 rootdrwxr-xr-x  27 root root   860 Mar 11 00:45 rundrwxr-xr-x   2 root root 12288 Mar  8 03:48 sbindrwxr-xr-x   2 root root  4096 Jun 29  2016 snapdrwxr-xr-x   2 root root  4096 Jul 19  2016 srvdr-xr-xr-x  13 root root     0 Mar 10 22:08 sysdrwxrwxrwt  12 root root  4096 Mar 11 00:45 tmpdrwxr-xr-x  11 root root  4096 Jul 19  2016 usrdrwxr-xr-x  14 root root  4096 Jul 19  2016 varlrwxrwxrwx   1 root root    29 Mar  8 03:56 vmlinuz -> boot/vmlinuz-4.4.0-66-genericlrwxrwxrwx   1 root root    29 Oct 24 23:51 vmlinuz.old -> boot/vmlinuz-4.4.0-45-generic


(2)ls是如何实现的?

目录是一种特殊的文件,目录是文件的列表。每个目录都至少包含两个特殊的项。“.”(当前目录),“..”(上一级目录)。

如何读取目录的信息?如何获取文件的信息?

man 3 readdir

struct dirent结构


man 3 opendir



有了上面的信息,我们很容易编写自己的ls命令。

(3)如何编写ls

#include<stdio.h>#include<dirent.h>#include<string.h>void show_ls(char filename[]);int main(int argc,char* argv[]){if(argc == 1)show_ls(".");while(--argc){printf("%s: \n",*++argv);show_ls(*argv);printf("\n");}return 0;}void show_ls(char filename[]){DIR* dir_ptr;  //the directorystruct dirent* direntp; //each entryif((dir_ptr = opendir(filename)) == NULL)fprintf(stderr,"ls1: cannot open%s \n",filename);while((direntp = readdir(dir_ptr)) != NULL)printf("%-10s",direntp->d_name);closedir(dir_ptr);}

我们已经把当前目录文件的名称打印出来了。(默认打印“.”,"..",这完成可以通过程序过滤,字典序可以通过qsort)

接下来实现ls  -l命令

ls -l命令需要文件的当前信息。


使用stat获得文件信息。


stat结构体


现在我们基本已经获得我们需要所有信息了,但是有些信息需要转换一下。才能跟shell标准下ls -l一致。

1.将模式串转为字符串。


在stat.h文件下可以看到很多关于模式判断的宏定义。这只是一部分,  可自行查阅。

#define S_IRUSR __S_IREAD       /* Read by owner.  */#define S_IWUSR __S_IWRITE      /* Write by owner.  */#define S_IXUSR __S_IEXEC       /* Execute by owner.  *//* Read, write, and execute by owner.  */#define S_IRWXU (__S_IREAD|__S_IWRITE|__S_IEXEC)#ifdef __USE_MISC# define S_IREAD        S_IRUSR# define S_IWRITE       S_IWUSR# define S_IEXEC        S_IXUSR#endif#define S_IRGRP (S_IRUSR >> 3)  /* Read by group.  */#define S_IWGRP (S_IWUSR >> 3)  /* Write by group.  */#define S_IXGRP (S_IXUSR >> 3)  /* Execute by group.  *//* Read, write, and execute by group.  */#define S_IRWXG (S_IRWXU >> 3)#define S_IROTH (S_IRGRP >> 3)  /* Read by others.  */
  相关实现。


2.用用户ID/组ID转成字符串。

在struct stat中,文件所有者都是以ID形式存在的,然而ls要求输出用户名和组名。

(1)用户名: man 3 getpwuid



(2)组名: man 3 getgrgid




有了上面的信息之后,编码实现ls - l

#include<stdio.h>#include<sys/stat.h>#include<dirent.h>#include<sys/types.h>#include<unistd.h>#include<stdlib.h>#include<string.h>#include<pwd.h>#include<grp.h>#include<time.h>void do_ls(char []);void do_stat(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);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[]){/*list files in directory called dirname*/DIR* dir_ptr;struct dirent * direntp; /*each entry*/if((dir_ptr = opendir(dirname)) == NULL)perror("opendir fails");while((direntp = readdir(dir_ptr)) !=NULL)do_stat(direntp->d_name);closedir(dir_ptr);}void do_stat(char* filename){struct stat info;if((stat(filename,&info)) == -1)perror(filename);elseshow_file_info(filename,&info);}void show_file_info(char* filename,struct stat * info_p){/*display the info about filename . the info is stored in struct at * info_p*/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 ",ctime(&info_p->st_mtime)+4);printf("%s\n",filename);}void mode_to_letters(int mode,char str[]){strcpy(str,"----------");if(S_ISDIR(mode)) str[0] = 'd';  //"directory ?"if(S_ISCHR(mode)) str[0] = 'c';  //"char decices"?if(S_ISBLK(mode)) str[0] = 'b';  //block device?//3 bits for userif(mode&S_IRUSR) str[1] = 'r';if(mode&S_IWUSR) str[2] = 'w';if(mode&S_IXUSR) str[3] = 'x';//3 bits for groupif(mode&S_IRGRP) str[4] = 'r';if(mode&S_IWGRP) str[5] = 'w';if(mode&S_IXGRP) str[6] = 'x';//3 bits for otherif(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* pw_ptr;static char numstr[10];if((pw_ptr =getpwuid(uid)) == NULL){sprintf(numstr,"%d",uid);printf("world");return numstr;}return pw_ptr->pw_name;}char* gid_to_name(gid_t gid){/*returns pointer to group number gid, used getgrgid*/struct group* grp_ptr;static char numstr[10];if((grp_ptr =getgrgid(gid)) == NULL){printf("hello wofjl");sprintf(numstr,"%d",gid);return numstr;}elsereturn grp_ptr->gr_name;}
运行结果:已经非常接近shell标准ls -l 命令


/** sol03.18.c ** ------------------------------------------------------------sol03.18.c is a versionof ls2.c that supports the -R option.  This program alsosupports the suid, sgid, and sticky bit handling from solution3.12.   ** ------------------------------------------------------------ * * ls2.c - supports -R option, *         handles suid, sgid, sticky bits, also fixes bug, * *purpose  list contents of directory or directories *action   if no args, use .  else list files in args *note     uses lstat and pwd.h and grp.h  *NOTE uses lstat to avoid chasing symlink loops * *build: cc sol03.18.c -o sol03.18 */#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<dirent.h>#include<sys/stat.h>void do_ls(char[],int);void dostat(char *,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 );main(int ac, char *av[]){intR_flag = 0;intanyfiles =  0;while ( --ac ){if ( strcmp("-R", *++av) == 0 )R_flag = 1;else {do_ls( *av , R_flag );anyfiles = 1;}}if ( !anyfiles )do_ls(".", R_flag);}void do_ls( char dirname[] , int subdirs )/* *list files in directory called dirname *lists subdirs if `subdirs' is true *First: list contents of dirname, then *       recurse to call each of the subdirs */{DIR*dir_ptr;/* the directory */struct dirent*direntp;/* each entry */char*fullpath;/* * get the problem case out of the way up front */if ( ( dir_ptr = opendir( dirname ) ) == NULL ){fprintf(stderr,"ls2: cannot open %s\n", dirname);return;}/* * ok, we can read the directory, make two passes through it */printf("%s:\n", dirname);fullpath = (char *)malloc(strlen(dirname) + 1 + MAXNAMLEN + 1);/* * pass 1: list the contents */while ( ( direntp = readdir( dir_ptr ) ) != NULL ){sprintf(fullpath,"%s/%s",dirname,direntp->d_name);dostat( fullpath, direntp->d_name );}/* * pass 2: rewind, traverse, and for each subdir, recurse */  /*The  rewinddir()  function  resets the position of the directory stream dirp to the     beginning of the directory*/if ( subdirs ){rewinddir(dir_ptr);while ( ( direntp = readdir( dir_ptr ) ) != NULL ){/* skip . and .. */if ( strcmp(".",direntp->d_name) == 0 ||     strcmp("..",direntp->d_name) == 0 )continue;sprintf(fullpath,"%s/%s",dirname,direntp->d_name);if ( isadir(fullpath) ){putchar('\n');do_ls( fullpath, subdirs );}}}/* all done with this dir, close up and go */closedir(dir_ptr);free(fullpath);}void dostat( char *fullpath, char *filename ){struct stat info;if ( lstat(fullpath, &info) == -1 )/* cannot stat */perror(filename);/* say why */else/* else show info */show_file_info(filename, &info);}void show_file_info( char *filename, struct stat *info_p )/* * display the info about 'filename'.  The info is stored in struct at *info_p */{char*uid_to_name(), *ctime(), *gid_to_name(), *filemode();voidmode_to_letters();        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 );}/* * utility functions *//* * This function takes a mode value and a char array * and puts into the char array the file type and the  * nine letters that correspond to the bits in mode. * NOTE: It now codes suid, sgid, and sticky bits *       see manual for details. */void mode_to_letters( int mode, char str[] ){    strcpy( str, "----------" );           /* default=no perms */    if ( S_ISDIR(mode) )  str[0] = 'd';    /* directory?       */    if ( S_ISCHR(mode) )  str[0] = 'c';    /* char devices     */    if ( S_ISBLK(mode) )  str[0] = 'b';    /* block device     */    if ( mode & S_IRUSR ) str[1] = 'r';    /* 3 bits for user  */    if ( mode & S_IWUSR ) str[2] = 'w';    if ( (mode & S_ISUID) && (mode & S_IXUSR) )str[3] = 's';    else if ( (mode & S_ISUID) && !(mode & S_IXUSR) )str[3] = 'S';    else if ( mode & S_IXUSR ) str[3] = 'x';    if ( mode & S_IRGRP ) str[4] = 'r';    /* 3 bits for group */    if ( mode & S_IWGRP ) str[5] = 'w';    if ( (mode & S_ISGID) && (mode & S_IXGRP) )str[6] = 's';    else if ( (mode & S_ISGID) && !(mode & S_IXGRP) )str[6] = 'S';    else if ( mode & S_IXGRP ) str[6] = 'x';    if ( mode & S_IROTH ) str[7] = 'r';    /* 3 bits for other */    if ( mode & S_IWOTH ) str[8] = 'w';    if ( (mode & S_ISVTX) && (mode & S_IXOTH) )str[9] = 't';    else if ( (mode & S_ISVTX) && !(mode & S_IXOTH) )str[9] = 'T';    else if ( mode & S_IXOTH ) str[9] = 'x';}#include<pwd.h>char *uid_to_name( uid_t uid )/*  *returns pointer to username associated with uid, uses getpw() */{structpasswd *getpwuid(), *pw_ptr;static  char numstr[10];if ( ( pw_ptr = getpwuid( uid ) ) == NULL ){sprintf(numstr,"%d", uid);return numstr;}elsereturn pw_ptr->pw_name ;}#include<grp.h>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;}elsereturn grp_ptr->gr_name;}/* * boolean: tells if arg names a directory */isadir(char *str){struct stat info;return ( lstat(str,&info) != -1 && S_ISDIR(info.st_mode) );}



0 0
原创粉丝点击