本文基于Ubuntu 12.04.4 (kernel version : 3.11.0-15-generic)
这几天在写一个Linux的基础命令——ls。
当然最终的效果可能和标准的GUN coreutils所提供的ls有些出入,但是在大致相同的情况下
了解其中的原理!
它的作用自然是明了的
先说说文件的属性相关的
文件的基本属性:文件类型,名字,大小,所有者以及所有者所在的组(具体就是他们拥有的权限),修改和访问时间,拥有的连接数。
这就是Linux中一个文件拥有的基本属性!
那么,这些的属性从哪里获得呢?一步一步来吧,这是和文件有关的命令,自然还是用man命令来帮助我们找找,看看不借助其他的工具(比如网络)的情况下我们能不能找到相关的信息。
首先要知道的是什么函数可以打开目录(或者说读取目录),借助联机帮助:
$ man -k direct
可以知道和目录有关的有很多的信息,继续找到我们想要的,
$ man -k direct | grep read
仔细查找找到了一个叫readdir的函数,这正是我们想要的东西!
继续搜索关于readdir的相关信息
$ man 3 reagier
关键的信息:
#include
struct direct *readdir(DIR *dirp);
在struct direct 中最重要的一个域就是 char d_name(还规定了大小为256)
readdir函数返回一个指向dirent结构的指针,只需要打印出dirent结构的d_name域就可以
得到文件的名字了。
(一)
接下来就来完成第一部分工作—-打印出文件的名字
现在我就把要查看的目录下的文件(或者目录)的名字都显示出来了
#include
#include
#include
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);
}
(二)
接下来我们要获得文件的相关的属性。
复习下文件(或者目录)的相关属性有哪些?
文件类型,大小,所有者以及所有者所在的组,相关的权限,修改和打开时间
怎么才能获得这些信息呢?还记得用man这个命令吗?
$ man -k file | grep status
通过返回的信息可以知道要查找文件的相关status(状态),继续进行以下操作
$ man 2 stat
坚持一下,胜利在望 (*^__^*)
现在可以看到要得到文件的属性(在系统里面的标准说法是状态,即就是status)
有以下:
(我的Linux系统上sys/types.h位于/usr/include/x86_64-linux-gnu)
#include
#include
#include
这是所需要的头文件;关于stat的man信息可谓是相当的丰富
在末尾处还给了贴心的给了个例子,调用stat()显示出选择的文件的相关属性信息,而这些属性信息都包含在结构体stat之中。
下面是这个示例程序:
#include
#include
#include
#include
#include
int
main(int argc, char *argv[])
{
struct stat sb;
if (argc != 2) {
fprintf(stderr, "Usage: %s \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);
}
这里需要说明的就是在switch语句那里可能不是很明白。这个 info.st_mode & S_IMFT 是什么意思呢?
这里就牵涉到了一个重要的概念叫做掩码!!
掩码会将不需要的字段置0,需要的字段不会发生变化,
在
#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文件*/
另外在sys/stat.h还提供了相关的宏,比如:
#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))
以上也差不多把Linux下面的常见的文件类型给列出来了
S_IFMT是一个掩码,它的值是0170000,可以用它来过滤前四位表示的文件类型。
下面的代码:
if( (info.st_mode & 0170000 ) == 0040000)
printf(“this is a directory! ”);
用过掩码将其他位置0,然后再与表示目录的代码作比较,从而判断是不是目录啦!
这里掩码的详细讲解不是重点,主要是明白info.st_mode & S_IMFT 是为了区分出文件类型!
区分出了文件类型,还要区分出文件用所有者,以及文件所有者所在的组对于文件所拥有的权限。
还是利用掩码技术得到文件所有者,所有者所在的组的权限,我们所需要的东西都在
不知道在编写第一个程序(就是那个stat的)时候,最后我们运行出来在mode的那行直接给出的是一串数字。
间接告诉我们这些数字是和所有者、所有者所在的组、其他人对于文件的权限(包括了读、写、执行)
接下来的这个函数是将mode那行的数字信息转换为我们熟悉的形式,就是这样:dr-xrw—wx。
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’;
}
到此为止我们已经处理了文件大小,文件名,模式(mode),最后修改时间
还有一个问题就是给出了uid和gid 如何找出相应的所有者名字和组的名字??
(三)将uid(用户ID)和gid(组ID)转换为字符串
通过阅读apue(UNIX环境高级编程)知道可以通过getpwuid访问用户信息;通过getgrpid访问用户组列表。就是这两个函数。
getpwuid定义在
指向struct passwd的指针
getgrgid定义在
好了我们的ls命令差不多就初具雏形了,
回想一下你做了什么呢?首先是找到文件的名字,然后是一系列的属性,
其实这里面最重要的是怎么去找这些东西——通过man命令去找!
现在就趁热打铁,写出你的ls吧!
Make your own ls !
#include
#include
#include
#include
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
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
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:[email protected]