您的位置:首页 > 运维架构 > Linux

Linux命令自己写 — ls

2014-06-25 18:57 183 查看
本文基于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;">#define	S_IFMT	0170000	/*文件类型*/
#define	S_IFREG	0100000	/*普通文件*/
#define	S_IFDIR	0040000	/*目录*/
#define	S_IFBLK	0060000	/*特殊的块文件*/
#define	S_IFCHR	0020000	/*字符设备文件*/
#define	S_IFIFO	0010000	/*管道文件*/
#define 	S_IFLNK	0120000	/*符号链接文件*/
#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( "." );
	else
		while ( --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();
	void	mode_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 )

{
	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 ;
}

#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;
	}
	else
		return grp_ptr->gr_name;
}


欢迎交流E-mail:1070443499@qq.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: