Linux命令自己写 — ls

本文基于Ubuntu 12.04.4 (kernel version : 3.11.0-15-generic)

这几天在写一个Linux的基础命令——ls。

当然最终的效果可能和标准的GUN coreutils所提供的ls有些出入,但是在大致相同的情况下

了解其中的原理!

它的作用自然是明了的

先说说文件的属性相关的

文件的基本属性:文件类型,名字,大小,所有者以及所有者所在的组(具体就是他们拥有的权限),修改和访问时间,拥有的连接数。

这就是Linux中一个文件拥有的基本属性!

那么,这些的属性从哪里获得呢?一步一步来吧,这是和文件有关的命令,自然还是用man命令来帮助我们找找,看看不借助其他的工具(比如网络)的情况下我们能不能找到相关的信息。

首先要知道的是什么函数可以打开目录(或者说读取目录),借助联机帮助:

<span style="font-family:Microsoft YaHei;font-size:14px;">$ man -k direct</span>

可以知道和目录有关的有很多的信息,继续找到我们想要的,

<span style="font-family:Microsoft YaHei;font-size:14px;">$ man -k direct | grep read</span>

仔细查找找到了一个叫readdir的函数,这正是我们想要的东西!

继续搜索关于readdir的相关信息

<span style="font-family:Microsoft YaHei;font-size:14px;">$ man 3 reagier</span>

关键的信息:

#include<drient.h>

struct direct *readdir(DIR *dirp);

在struct direct 中最重要的一个域就是 char d_name(还规定了大小为256)

readdir函数返回一个指向dirent结构的指针,只需要打印出dirent结构的d_name域就可以

得到文件的名字了。

(一)

接下来就来完成第一部分工作—-打印出文件的名字

现在我就把要查看的目录下的文件(或者目录)的名字都显示出来了

<span style="font-family:Microsoft YaHei;font-size:14px;">#include<stdio.h>#include<stdlib.h>#include<dirent.h>void do_ls( char *dri_name ); int main(int argc, char *argv[]) {     if( argc == 1 )        do_ls(".");      else     {        do_ls( argv[1] );      }    return 0; } void do_ls( char *dir_name ) {     DIR *dir_ptr;     struct dirent *dir_oper;     /* open the file */     dir_ptr = opendir(dir_name);   if( dir_ptr == NULL)        fprintf(stderr, "Your-ls:cannot open %s\n", dir_name);    /* read the file */while( ( dir_oper = readdir( dir_ptr ) ) != NULL)printf(“%s\n”, dir_oper->d_name);closedir( dir_ptr); }</span>

(二)

接下来我们要获得文件的相关的属性。

复习下文件(或者目录)的相关属性有哪些?

文件类型,大小,所有者以及所有者所在的组,相关的权限,修改和打开时间

怎么才能获得这些信息呢?还记得用man这个命令吗?

<span style="font-family:Microsoft YaHei;font-size:14px;">$ man -k file | grep status</span>

通过返回的信息可以知道要查找文件的相关status(状态),继续进行以下操作

<span style="font-family:Microsoft YaHei;font-size:14px;">$ man 2 stat</span>

坚持一下,胜利在望 (*^__^*)

现在可以看到要得到文件的属性(在系统里面的标准说法是状态,即就是status)

有以下:

(我的Linux系统上sys/types.h位于/usr/include/x86_64-linux-gnu)

#include<sys/types.h>

#include<sys/stat.h>

#include<unistd.h>

这是所需要的头文件;关于stat的man信息可谓是相当的丰富

在末尾处还给了贴心的给了个例子,调用stat()显示出选择的文件的相关属性信息,而这些属性信息都包含在结构体stat之中。

下面是这个示例程序:

<span style="font-family:Microsoft YaHei;font-size:14px;">       #include <sys/types.h>       #include <sys/stat.h>       #include <time.h>       #include <stdio.h>       #include <stdlib.h>       int       main(int argc, char *argv[])       {           struct stat sb;           if (argc != 2) {               fprintf(stderr, "Usage: %s <pathname>\n", argv[0]);               exit(EXIT_FAILURE);           }           if (stat(argv[1], &sb) == -1) {               perror("stat");               exit(EXIT_FAILURE);           } printf("File type:                "); switch (sb.st_mode & S_IFMT) {           case S_IFBLK:  printf("block device\n");            break;           case S_IFCHR:  printf("character device\n");        break;           case S_IFDIR:  printf("directory\n");               break;case S_IFIFO:  printf("FIFO/pipe\n");               break;           case S_IFLNK:  printf("symlink\n");                 break;           case S_IFREG:  printf("regular file\n");            break;           case S_IFSOCK: printf("socket\n");                  break;           default:       printf("unknown?\n");                break;           }           printf("I-node number:            %ld\n", (long) sb.st_ino);           printf("Mode:                     %lo (octal)\n",                   (unsigned long) sb.st_mode);           printf("Link count:               %ld\n", (long) sb.st_nlink);           printf("Ownership:                UID=%ld   GID=%ld\n",                   (long) sb.st_uid, (long) sb.st_gid);           printf("Preferred I/O block size: %ld bytes\n",                   (long) sb.st_blksize);           printf("File size:                %lld bytes\n",                   (long long) sb.st_size);           printf("Blocks allocated:         %lld\n",           (long long) sb.st_blocks);           printf("Last status change:       %s", ctime(&sb.st_ctime));           printf("Last file access:         %s", ctime(&sb.st_atime));           printf("Last file modification:   %s", ctime(&sb.st_mtime));           exit(EXIT_SUCCESS);       }</span>

这里需要说明的就是在switch语句那里可能不是很明白。这个 info.st_mode & S_IMFT 是什么意思呢?

这里就牵涉到了一个重要的概念叫做掩码!!

掩码会将不需要的字段置0,需要的字段不会发生变化,

在<sys/stat.h>这个头文件中定义以下内容:

<span style="font-family:Microsoft YaHei;font-size:14px;">#defineS_IFMT0170000/*文件类型*/#defineS_IFREG0100000/*普通文件*/#defineS_IFDIR0040000/*目录*/#defineS_IFBLK0060000/*特殊的块文件*/#defineS_IFCHR0020000/*字符设备文件*/#defineS_IFIFO0010000/*管道文件*/#define S_IFLNK0120000/*符号链接文件*/#define S_IFSOCK 0140000 /*socket文件*/</span>

另外在sys/stat.h还提供了相关的宏,比如:

<span style="font-family:Microsoft YaHei;font-size:14px;">#define S_ISFIFO(m)(((m)&(0170000)) == (0010000))#define S_ISDIR(m) (((m)&(0170000)) == (0040000))#define S_ISBLK(m) (((m)&(0170000)) == (0020000))#define S_ISREG(m) (((m)&(0170000)) == (0060000))</span>

以上也差不多把Linux下面的常见的文件类型给列出来了

S_IFMT是一个掩码,它的值是0170000,可以用它来过滤前四位表示的文件类型。

下面的代码:

if( (info.st_mode & 0170000 ) == 0040000)

printf(“this is a directory! ”);

用过掩码将其他位置0,然后再与表示目录的代码作比较,从而判断是不是目录啦!

这里掩码的详细讲解不是重点,主要是明白info.st_mode & S_IMFT 是为了区分出文件类型!

区分出了文件类型,还要区分出文件用所有者,以及文件所有者所在的组对于文件所拥有的权限。

还是利用掩码技术得到文件所有者,所有者所在的组的权限,我们所需要的东西都在 <sys/stat.h>中定义好了

不知道在编写第一个程序(就是那个stat的)时候,最后我们运行出来在mode的那行直接给出的是一串数字。

间接告诉我们这些数字是和所有者、所有者所在的组、其他人对于文件的权限(包括了读、写、执行)

接下来的这个函数是将mode那行的数字信息转换为我们熟悉的形式,就是这样:dr-xrw—wx。

<span style="font-family:Microsoft YaHei;font-size:14px;">void mode_to_letter( int mode, char str[]){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’;}</span>

到此为止我们已经处理了文件大小,文件名,模式(mode),最后修改时间

还有一个问题就是给出了uid和gid 如何找出相应的所有者名字和组的名字??

(三)将uid(用户ID)和gid(组ID)转换为字符串

通过阅读apue(UNIX环境高级编程)知道可以通过getpwuid访问用户信息;通过getgrpid访问用户组列表。就是这两个函数。

getpwuid定义在<pwd.h>,又可以使用man命令得到你想要的关于getpwuid的信息。getpwuid需要一个uid作为参数,并且返回一个

指向struct passwd的指针

getgrgid定义在<grp.h>,其他的和getpwuid类似。

好了我们的ls命令差不多就初具雏形了,

回想一下你做了什么呢?首先是找到文件的名字,然后是一系列的属性,

其实这里面最重要的是怎么去找这些东西——通过man命令去找!

现在就趁热打铁,写出你的ls吧!

Make your own ls !

#include<stdio.h>#include<sys/types.h>#include<dirent.h>#include<sys/stat.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 );main(int ac, char *av[]){if ( ac == 1 )do_ls( "." );elsewhile ( --ac ){printf("%s:\n", *++av );do_ls( *av );}}void do_ls( char dirname[] ){DIR*dir_ptr;/* the directory */struct dirent*direntp;/* each entry */if ( ( dir_ptr = opendir( dirname ) ) == NULL )fprintf(stderr,"ls1: cannot open %s\n", dirname);else{while ( ( direntp = readdir( dir_ptr ) ) != NULL )dostat( direntp->d_name );closedir(dir_ptr);}}void dostat( char *filename ){struct stat info;if ( stat(filename, &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 ){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 );}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_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_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_IXOTH ) str[9] = 'x';}#include<pwd.h>char *uid_to_name( uid_t uid ){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 ){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;}

欢迎交流E-mail:1070443499@qq.com

好好的管教你自己,不要管别人。

Linux命令自己写 — ls

相关文章:

你感兴趣的文章:

标签云: