需求分析
ls -l 的使用方式
ls -l 的基本使用方式有两种,分别是ls -l +文件名、ls -l +目录,如下图所示:
根据图片中得到的结果分析出,ls -l 命令得到的结果包含有文件类型,权限信息,链接数,用户名,组名,文件大小,最后修改时间,文件名称以及链接文件的目标文件。
故程序的设计要求就是通过用户传入一个文件名或者路径,解析出该文件或者该路径下所有文件的相关信息并按照格式打印到终端上。
功能实现
目录流
打开目录
读取目录
目录流的关闭
获取文件的属性
stat这个结构体是用来描述一个linux系统文件系统中的文件属性的结构,stat结构体的详细属性如下:
struct stat {
mode_t st_mode; //文件对应的模式,文件,目录等
ino_t st_ino; //inode节点号
dev_t st_dev; //设备号码
dev_t st_rdev; //特殊设备号码
nlink_t st_nlink; //文件的连接数
uid_t st_uid; //文件所有者
gid_t st_gid; //文件所有者对应的组
off_t st_size; //普通文件,对应的文件字节数
time_t st_atime; //文件最后被访问的时间
time_t st_mtime; //文件内容最后被修改的时间
time_t st_ctime; //文件状态改变时间
blksize_t st_blksize; //文件内容对应的块大小
blkcnt_t st_blocks; //文件内容对应的块数量
};
定义一个struct stat sbuf;通过lstat(s,&sbuf)来获取文件属性,其中代表文件路径。
文件的类型
stat结构体中的st_mode则 则定义了下列8个宏:
S_IFMT 0170000 文件类型的位遮罩
S_IFSOCK 0140000 scoket
S_IFLNK 0120000 符号连接
S_IFREG 0100000 一般文件
S_IFBLK 0060000 区块装置
S_IFDIR 0040000 目录
S_IFCHR 0020000 字符装置
S_IFIFO 0010000 先进先出
st-mode & S_IFMT 的结果即为七大文件类型的宏值之一;所以可以使用switch-case语句来判断文件的类型并进行打印。
//查看文件类型
switch(sbuf.st_mode&S_IFMT){
case S_IFSOCK: printf("s");break;
case S_IFCHR: printf("c");break;
case S_IFLNK: printf("l");break;
case S_IFREG: printf("-");break;
case S_IFBLK: printf("b");break;
case S_IFDIR: printf("d");break;
case S_IFIFO: printf("p");break;
}
另外, 上述的文件类型在POSIX中也定义了检查这些类型的宏定义:
S_ISLNK (st_mode) 判断是否为符号连接
S_ISREG (st_mode) 是否为一般文件
S_ISDIR (st_mode) 是否为目录
S_ISCHR (st_mode) 是否为字符装置文件
S_ISBLK (s3e) 是否为先进先出
S_ISSOCK (st_mode) 是否为socket使用st_mode成员判断文件类型:
if((sb.st_mode & S_IFMT) ==S_IFLNK)
{
printf("连接文件\n");
}if (S_ISREG(sb.st_mode))
{
printf("普通文件\n");
}
文件的权限
S_IRUSR 00400 文件所有者具可读取权限 二进制:100 000 000 1<<8
S_IWUSR 00200 文件所有者具可写入权限 二进制:010 000 000 1<<7
S_IXUSR 00100 文件所有者具可执行权限 二进制:001 000 000 1<<6
S_IRGRP 00040 用户组具可读取权限 二进制:000 100 000 1<<5
S_IWGRP 00020 用户组具可写入权限 二进制:000 010 000 1<<4
S_IXGRP 00010 用户组具可执行权限 二进制:000 001 000 1<<3
S_IROTH 00004 其他用户具可读取权限 二进制:000 000 100 1<<2
S_IWOTH 00002 其他用户具可写入权限 二进制:000 000 010 1<<1
S_IXOTH 00001 其他用户具可执行权限 二进制:000 000 001 1<<0
根据以上关系得出,st_mode &(1<<n)即为该文件的各个权限(0=<n<8=8)。
//查看权限
int n=8;
for(;n>=0;n--)
if(sbuf.st_mode&(1<<n))
switch(n%3){
case 2: printf("r");break;
case 1: printf("w");break;
case 0: printf("x");break;
}
else{
printf("-");
}
文件的链接数
通过stat结构体中st_nlink属性可以获取文件的链接数。
printf(" %d",sbuf.st_nlink);
文件所属的用户、用户组
用户信息与用户组信息存储分别在struct stat中的st_uid和 st_gid中
getpwuid函数:解码所有者信息
#include <sys/types.h>
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);
struct passwd {
char *pw_name; /* username */
char *pw_passwd; /* user password */
uid_t pw_uid; /* user ID */
gid_t pw_gid; /* group ID */
char *pw_gecos; /* user information */
char *pw_dir; /* home directory */
char *pw_shell; /* shell program */
};
getgrgid函数:解码所属组信息
#include <sys/types.h>
#include <grp.h>struct group *getgrgid(gid_t gid);
struct group {
char *gr_name; /* group name */
char *gr_passwd; /* group password */
gid_t gr_gid; /* group ID */
char **gr_mem; /* NULL-terminated array of pointers
to names of group members */
};
//查看用户名,组名
struct passwd *puser;
puser=getpwuid(sbuf.st_uid);
printf(" %s",puser->pw_name);
struct group *pgroup;
pgroup=getgrgid(sbuf.st_gid);
printf(" %s",pgroup->gr_name);
文件大小
文件大小存储在struct stat 结构体中st_size属性中
//查看大小
printf(" %ld",sbuf.st_size);
文件最后修改时间
文件的最后修改时间信息存储在struct stat 结构体中 st_mtime属性中 ,通过localtime解码时间信息。
struct tm *now_time;
now_time=localtime(&sbuf.st_mtime);
printf(" %d-%d-%d %d:%d"
,now_time->tm_year+1900
,now_time->tm_mon+1,now_time->tm_mday
,now_time->tm_hour,now_time->tm_min);
文件名
//查看文件名
printf(" %s",s);
链接文件的目标文件
//链接指向目标文件
char buf[128]={0};
if((sbuf.st_mode&S_IFMT)==S_IFLNK)
{
readlink(s,buf,sizeof(buf));
printf("-->%s\n",buf);
}
else
printf("\n");
程序设计
设计思想及源码
主程序中使用命令行参数的方式传入文件名或目录,故需要分情况讨论,传入不同的参数执行不同的函数。
int main(int argc, char* argv[])
{
if(argc!=2){
printf("请输入两个参数!!!\n");
return -1;
}
struct stat sbuf;
lstat(argv[1],&sbuf);
if(S_ISDIR(sbuf.st_mode))
{ //如果是目录
dirPath(argv[1]);
}
else
{ //如果是文件名
filePath(argv[1]);
}
return 0;
}
如果传入的是文件名:
void filePath(const char* s)
{
struct stat sbuf;
lstat(s,&sbuf);
//查看文件类型
switch(sbuf.st_mode&S_IFMT){
case S_IFSOCK: printf("s");break;
case S_IFCHR: printf("c");break;
case S_IFLNK: printf("l");break;
case S_IFREG: printf("-");break;
case S_IFBLK: printf("b");break;
case S_IFDIR: printf("d");break;
case S_IFIFO: printf("p");break;
}
//查看权限
int n=8;
for(;n>=0;n--)
if(sbuf.st_mode&(1<<n))
switch(n%3){
case 2: printf("r");break;
case 1: printf("w");break;
case 0: printf("x");break;
}
else{
printf("-");
}
//查看链接数
printf(" %d",sbuf.st_nlink);
//查看用户名,组名
struct passwd *puser;
puser=getpwuid(sbuf.st_uid);
printf(" %s",puser->pw_name);
struct group *pgroup;
pgroup=getgrgid(sbuf.st_gid);
printf(" %s",pgroup->gr_name);
//查看大小
printf(" %ld",sbuf.st_size);
//查看最后修改时间
struct tm *now_time;
now_time=localtime(&sbuf.st_mtime);
printf(" %d-%d-%d %d:%d"
,now_time->tm_year+1900
,now_time->tm_mon+1,now_time->tm_mday
,now_time->tm_hour,now_time->tm_min);
//查看文件名
printf(" %s",s);
//链接指向目标文件
char buf[128]={0};
if((sbuf.st_mode&S_IFMT)==S_IFLNK)
{
readlink(s,buf,sizeof(buf));
printf("-->%s\n",buf);
}
else
printf("\n");
}
如果传入的是目录:
首先需要打开目录,然后依次读取目录中的文件进行文件属性获取然后打印,读取结束后关闭目录。
void dirPath(const char* s)
{
DIR* dirp=opendir(s);
if(dirp==NULL)
{
perror("opendir");
return;
}
struct dirent* fp;
while(NULL!=(fp=readdir(dirp)))
{
if(fp->d_name[0]=='.')
{
continue;
}
struct stat sbuf;
lstat(fp->d_name,&sbuf);
//查看文件类型
switch(sbuf.st_mode&S_IFMT){
case S_IFSOCK: printf("s");break;
case S_IFCHR: printf("c");break;
case S_IFLNK: printf("l");break;
case S_IFREG: printf("-");break;
case S_IFBLK: printf("b");break;
case S_IFDIR: printf("d");break;
case S_IFIFO: printf("p");break;
}
//查看权限
int n=8;
for(;n>=0;n--)
if(sbuf.st_mode&(1<<n))
switch(n%3){
case 2: printf("r");break;
case 1: printf("w");break;
case 0: printf("x");break;
}
else{
printf("-");
}
//查看链接数
printf(" %d",sbuf.st_nlink);
//查看用户名,组名
struct passwd *puser;
puser=getpwuid(sbuf.st_uid);
printf(" %s",puser->pw_name);
struct group *pgroup;
pgroup=getgrgid(sbuf.st_gid);
printf(" %s",pgroup->gr_name);
//查看大小
printf(" %6ld",sbuf.st_size);
//查看最后修改时间
struct tm *now_time;
now_time=localtime(&sbuf.st_mtime);
printf(" %d-%d-%d %2d:%2d"
,now_time->tm_year+1900
,now_time->tm_mon+1,now_time->tm_mday
,now_time->tm_hour,now_time->tm_min);
//查看文件名
printf(" %s",fp->d_name);
//链接指向目标文件
char buf[128]={0};
if((sbuf.st_mode&S_IFMT)==S_IFLNK)
{ //读取链接文件的指向
readlink(fp->d_name,buf,sizeof(buf));
printf("-->%s\n",buf);
}
else
printf("\n");
}
closedir(dirp);
}
实现效果
可以看到对于文件名,./a.out ./a.out和 ls -l ./a.out 的效果一致
对于目录,./a.out ./ 和 ls -l ./的效果一致