一:背景
linux下的ls可以实现什么效果呢,ls有很多的选项,最为常用的选项莫过于是-l选项,列出所有文件的详细信息。本文也着重去实现ls -l。首先看下ls -l的效果。本文将会完整的去描述怎么样一步一步去实现。
[root@bogon unix]# ls -l total 116 -rw-r--r--. 1 root root 1063 Jul 6 20:18 aaaa -rwxr-xr-x. 1 root root 9811 Jul 18 22:17 a.out -rw-r--r--. 1 root root 1474 Jul 10 21:58 cp1.c -rw-r--r--. 1 root root 386 Jul 10 21:54 exis.c -rw-r--r--. 1 root root 601 Jul 15 22:22 fileinfo.c -rw-r--r--. 1 root root 515 Jul 6 21:39 logout_tty.c -rw-r--r--. 1 root root 755 Jul 12 15:21 ls1.c -rw-r--r--. 1 root root 2625 Jul 18 22:17 ls2.c ----------. 1 root root 1063 Jul 3 21:31 more01.c -rwxrwxrwx. 1 root root 1651 Jul 5 21:48 more02.c -rw-r--r--. 1 root root 270 Jul 15 22:16 stattest.c -rw-r--r--. 1 root root 262 Jul 4 21:51 test1.c -rw-r--r--. 1 root root 1337 Jul 12 14:19 test2.c -rw-r--r--. 1 root root 140 Jul 12 14:11 test3.c -rw-r--r--. 1 root root 527 Jul 3 22:19 test.c -rw-r--r--. 1 root root 169 Jul 7 22:42 ttytest.c -rw-r--r--. 1 root root 955 Jul 7 20:54 utmplib.c -rw-r--r--. 1 root root 87 Jul 6 21:13 utmplib.h -rw-r--r--. 1 root root 2688 Jul 6 21:12 utmplib.o -rwxr-xr-x. 1 root root 980 Jul 12 14:22 who1.c -rwxr-xr-x. 1 root root 9576 Jul 6 21:13 who2 -rw-r--r--. 1 root root 1791 Jul 12 13:15 who2.c -rw-r--r--. 1 root root 2720 Jul 6 21:13 who2.o -rw-r--r--. 1 root root 1424 Jul 12 12:45 who_am_i.c -rw-r--r--. 1 root root 80 Jul 7 22:43 whoami.c
这是一个使用ls -l选项列出的的文件信息,可以看出ls -l列出了所有文件的 属性,连接,属主,属组,大小,ctime以及文件名等信息。要获取文件的这些信息并进去适当的格式化,就是本文所要做的事情了。
二:开始实现
要想自己实现ls -l就得知道可以通过什么系统调用可以获取到这些文件信息。
首先自然是要通过man来查找相关的系统调用。
man -k file|grep status man -k file|grep infomation man -k file |grep info
不停的换上多个关键词来搜索,通过上面的搜索就可以得到stat这个系统调用来获取文件信息。
紧接着就可以man 2 stat来获取系统调用的详细使用方法。
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *path, struct stat *buf);
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 */
};
到了这里处理起来似乎很顺畅了,只要获取相应字段进行格式处理就OK了。
三:权限处理
st_mode就是文件的权限部分,对于这个部分要把其处理成ls -l列出的那种形式。
st_mode本身就是一个16位的二进制,前四位是文件的类型,紧接着三位是文件的特殊权限,最后的九位就是ls -l列出来的九个权限。如何把st_mode转换成对应的权限就是权限处理这块的关键了。
linux本身提供了很多测试宏来测试文件的类型的。
#define __S_IFMT 0170000 /* These bits determine file type. */
/* File types. */
#define __S_IFDIR 0040000 /* Directory. */
#define __S_IFCHR 0020000 /* Character device. */
#define __S_IFBLK 0060000 /* Block device. */
#define __S_IFREG 0100000 /* Regular file. */
#define __S_IFIFO 0010000 /* FIFO. */
#define __S_IFLNK 0120000 /* Symbolic link. */
#define __S_IFSOCK 0140000 /* Socket. */
# define S_IFMT __S_IFMT
# define S_IFDIR __S_IFDIR
# define S_IFCHR __S_IFCHR
# define S_IFBLK __S_IFBLK
# define S_IFREG __S_IFREG
利用上面的测试宏就可以判断文件的类型,至于文件的权限部分可以使用掩码的方式来处理。
具体代码如下:
void mode_to_letters(int mode,char str[])
{
//S_IS***测试宏
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';
}
四:ctime时间处理
stat中的st_mtime是一个时间戳,而ls -l显示出来的是一个格式化后的字符串,所以需要对st_mtime字段进行格式化处理。
ctime接受一个time_t类型的值,可将其转换为Fri Jul 18 22:12:43 2014这样的格式,其中最前面的星期几不是我们要的,只要其后的值。代码如下
printf("%.12s",4+ctime(&info_p->st_mtime));
info_p是一个文件的stat结构体。
五:属组和属主处理
通过stat结构体的注释信息可以看出,stat只能获取文件的uid和gid并不是uid gid对应的用户或组的名称。
uid找到对应的用户名是可以通过getpwuid这个系统调用获取,这个系统调用接受一个uid返回一个passwd的结构体,这个结构体成员如下:
char *pw_name User’s login name.
uid_t pw_uid Numerical user ID.
gid_t pw_gid Numerical group ID.
char *pw_dir Initial working directory.
char *pw_shell Program to use as shell.
pw_name字段就是我们要的。实现代码如下:
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;
}
gid对应的组名同样可以找到一个系统调用来获取,通过man我发现可以通过getgrgid()系统调用传入一个gid
返回这个gid对应的一个group结构体,这个结构体成员如下:
char *gr_name The name of the group.
gid_t gr_gid Numerical group ID.
char **gr_mem Pointer to a null-terminated array of character
pointers to member names.
实现gid到组名的转换的代码如下:
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;
}
到此为止,ls -l基本就完成了。后续再进行完善。
六:完整代码示例
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<dirent.h>
#include<stdio.h>
#include<stdlib.h>
#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(uid_t);
int main(int argc,char *argv[])
{
if(argc == 1)
do_ls(".");
else
/*
*依次遍历每一个参数,打印目录下的所有文件
*/
while(--argc){
printf("%s:\n",*++argv);
do_ls(*argv);
}
}
void do_ls(char dirname[])
{
/*
*定义一个目录流,和目录流结构体保存读到的结果。
*/
DIR *dir_ptr;
struct dirent *direntp;
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);
//printf("%s\n",direntp->d_name);
//关闭目录流
closedir(dir_ptr);
}
}
//获取文件信息stat结构体
void dostat(char *filename)
{
struct stat info;
if(stat(filename,&info) == -1)
perror(filename);
else
//分析stat结构体
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);
}
//分析mode权限
void mode_to_letters(int mode,char str[])
{
//S_IS***测试宏
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';
}
// uid gid to name group
#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;
}
转载于:https://blog.51cto.com/forlinux/1440088