准备
功能描述
-a:全部文件,连同隐藏文件一同列出来
-l:详细信息显示,包含文件的属性与权限等数据
-R:连同子目录一同列出来,等于该目录下的所有文件都会显示出来
-r:以文件名反序排列并输出目录内容列表
用lunix文件和目录函数遍历一个目录下的所有文件,包括子目录,并分别把信息输出到文件和标准输出上。列举每个文件的权限相关信息,列举每个目录的相关信息。
实现要求
* -R需要实现对根目录的查询
* 需要实现三种参数的随意组合
* 在所有目录下都可使用自己的my_ls
* 界面美观( 输出对齐等)
* 能屏蔽Ctrl+c杀死程序
* 按照文件类型以及权限的不同分颜色输出
* 加入其他参数,例如: -t, -r, -i,-s等
主函数流程图
ls输出格式
索引节点号(-i) | 区块(-s) | 属性 | 链接数 | 拥有者 | 所属群组 | 文档容量大小 | 文档最后修改时期 | 文档名 |
---|
实现
stat函数
目标 | 得到文件的属性 |
---|---|
头文件 | #include<sys/stat.h> |
函数原型 | int result = stat(char *filename,struct stat * buf) |
参数 | filename 文件名 buf 指向buffter的指针 |
返回值 | -1 遇到错误 0 成功返回 |
stat函数把filename的信息复制到指针 buf 所指的结构中
struct stat的成员变量
st_mode | 文件类型权限和许可权限 |
---|---|
st_uid | 用户所有者的id |
st_gid | 所属组的id |
st_size | 所占的字节数 |
st_nlink | 文件链接数 |
st_mtime | 文件最后修改时间 |
st_atime | 文件最后访问时间 |
st_ctime | 文件属性最后改变时间 |
代码实现
#include <stdio.h>
#include<stdlib.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include<time.h>
#include<string.h>
#include<pwd.h>
#include<grp.h>
#include<limits.h>
#include<unistd.h>
#include<errno.h>
#define LS_NONE 0
#define LS_A 1
#define LS_L 2
#define LS_R 4//-R:连同子目录一同列出来,等于该目录下的所有文件都会显示出来,需要实现对根目录的查询
#define LS_r 8
#define MAXROWLEN 80
char PATH[PATH_MAX+1]; //用于存储路径
int flag;
int g_leave_len=MAXROWLEN,g_maxlen;
void my_err(const char *err_string,int line);
void display_attribute(struct stat buf,char *name);//获取文件属性并打印
void display_single(char *name); //没有参数
void display(int flag,char *pathname); //绝对路径显示
void display_dir(int flag_param,char *path); //目录
void cprint(char* name,mode_t st_mode);
void displayR_attribute(char* name);
int main(int argc,char **argv)
{
int i=0,j=0,k,num=0;
char path[PATH_MAX+1];
char param[32];//保存命令行参数
int flag_param=LS_NONE;
struct stat buf;
for(i=1;i<argc;i++)
{
if(argv[i][0]=='-')
{
for(k=1;k<strlen(argv[i]);k++,j++)
{
param[j]=argv[i][k];
}
}
num++;
}
for(i=0;i<j;i++)
{
if(param[i]=='a')
flag_param |=LS_A;
else if(param[i]=='l')
{
flag_param |=LS_L;
continue;
}
else{
printf("my_ls: invalid option -%c\n",param[i]);
exit(1);
}
}
param[i]='\0';
//如果没有输入文件名或目录,就显示当前目录
if((num+1)==argc)
{
strcpy(path, "./");
path[2]='\0';
display_dir(flag_param,path);
return 0;
}
i=1;
do{
//如果不是目标文件名或目录,解析下一个命令行参数
if(argv[i][0]=='-')
{
i++;
continue;
}
else
{
strcpy(path,argv[i]);
//如果目标文件或目录不存在,报错并退出程序
if(stat(path,&buf)==-1)
{
my_err("stat",__LINE__);
}
if(S_ISDIR(buf.st_mode))
{
if(path[strlen(argv[i]-1)]!='/')
{
path[strlen(argv[i])]='/';
path[strlen(argv[i+1])]='\0';
}
else
{
path[strlen(argv[i])]='\0';
}
display_dir(flag_param,path);
i++;
}
else
{
display(flag_param,path);
i++;
}
}
}while(i<argc);
return 0;
}
void my_err(const char *err_string,int line)
{
fprintf(stderr,"line:%d",line);
perror(err_string);
exit(1);
}
void display_attribute(struct stat buf,char *name)
{
char buf_time[32];
struct passwd *uid;
struct group *gid;
char str[10]={
0};
strcpy(str,"----------");
if(S_ISDIR(buf.st_mode)) str[0]='d'; //S_ISDIR():功能是判断一个路径是否为目录Test for a directory.
if(S_ISCHR(buf.st_mode)) str[0]='c'; //Test for a character special file.
if(S_ISBLK(buf.st_mode)) str[0]='b'; //Test for a block special file.
if(S_ISLNK(buf.st_mode)) str[0]='l';
if(S_ISREG(buf.st_mode)) str[0]='-';
if(S_ISFIFO(buf.st_mode)) str[0]='f';
//if(S_ISSOCK(mode)) str[0]='s';
if((buf.st_mode & S_IRUSR)) str[1]='r'; //Read permission, owner. 可读权限,所有者
if((buf.st_mode & S_IWUSR)) str[2]='w'; //Write permission, owner. 可写权限
if((buf.st_mode & S_IXUSR)) str[3]='x'; //Execute/search permission, owner. 可执行权限
if((buf.st_mode & S_IRGRP)) str[4]='r'; //Read permission, group. 可读,用户组
if((buf.st_mode & S_IWGRP)) str[5]='w';
if((buf.st_mode & S_IXGRP)) str[6]='x';
if((buf.st_mode & S_IROTH)) str[7]='r'; //其他人
if((buf.st_mode & S_IWOTH)) str[8]='w';
if((buf.st_mode & S_IXOTH)) str[9]='x';
printf("%s",str);
uid=getpwuid(buf.st_uid);
gid=getgrgid(buf.st_gid);
printf("%4zu ",buf.st_nlink);
printf("%-8s",uid->pw_name);
printf("%-8s",gid->gr_name);
printf("%6zu",buf.st_size);
strcpy(buf_time,ctime(&buf.st_mtime));
buf_time[strlen(buf_time)-1]='\0';//去掉换行符
printf(" %s",buf_time);
}
void display_single(char *name) //在没有-l选项时,打印一个文件名,打印时上下行对齐
{
//int len ;
struct stat buf;
if(lstat(name,&buf)==-1)
{
return;
}
if(g_leave_len<g_maxlen)
{
printf("\n");
g_leave_len=MAXROWLEN;
}
cprint(name,buf.st_mode); //根据文件的不同类型显示不同颜色
g_leave_len=g_leave_len-(g_maxlen+2);
}
void cprint(char* name,mode_t st_mode)
{
if(S_ISLNK(st_mode)) //链接文件
printf("\033[1;36m%-*s\033[0m",g_maxlen,name);
else if(S_ISDIR(st_mode)&&(st_mode&000777)==0777) //满权限的目录
printf("\033[1;34;42m%-*s \033[0m",g_maxlen,name);
else if(S_ISDIR(st_mode)) //目录
printf("\033[1;34m%-*s \033[0m",g_maxlen,name);
else if(st_mode&S_IXUSR||st_mode&S_IXGRP||st_mode&S_IXOTH) //可执行文件
printf("\033[1;32m%-*s \033[0m",g_maxlen,name);
else //其他文件
printf("%*s ",g_maxlen,name);
}
void display(int flag,char* pathname)//根据命令行参数和完整路径显示目标文件
{
int i,j;
struct stat buf;
char name[NAME_MAX+1];
//从路径解析出文件名
for(i=0,j=0;i<strlen(pathname);i++)
{
if(pathname[i]=='/')
{
j=0;
continue;
}
name[j++]=pathname[i];
}
name[j] = '\0';
//用lstat方便解析链接文件
if(lstat(pathname,&buf)==-1)
{
my_err("stat",__LINE__);
}
switch (flag)
{
case LS_NONE:
if(name[0]!='.') display_single(name);
break;
case LS_A:
display_single(name);
break;
case LS_L:
if(name[0]!='.')
{
display_attribute(buf,name);
printf(" %-s\n",name);
}
case LS_A+LS_L:
display_attribute(buf,name);
printf(" %-s\n",name);
break;
default:
break;
}
}
void display_dir(int flag_param,char *path)
{
DIR *dir;
struct dirent *ptr;
int count=0;
char filenames[256][PATH_MAX+1],temp[PATH_MAX+1];
//获取该目录下的文件总数和最长的文件名
dir=opendir(path);
if(dir==NULL) my_err("opendir",__LINE__);
while((ptr=readdir(dir))!=NULL)
{
if(g_maxlen<strlen(ptr->d_name))
g_maxlen=strlen(ptr->d_name);
count++;
}
closedir(dir);
if(count>256)
my_err("too many files under this dir",__LINE__);
int i,j,len=strlen(path);
//获取该目录下所有的文件名
dir = opendir(path);
for(i=0;i<count;i++)
{
if((ptr=readdir(dir))==NULL)
my_err("readdir",__LINE__);
strncpy(filenames[i],path,len);
filenames[i][len]='\0';
strcat(filenames[i],ptr->d_name);
filenames[i][len+strlen(ptr->d_name)]='\0';
}
//使用冒泡法对文件名进行排序,然后储存在filename中
for(i=0;i<count;i++)
{
for(j=0;j<count-1-i;j++)
{
if(strcmp(filenames[j],filenames[j+1])>0)
{
strcpy(temp,filenames[j+1]);
temp[strlen(filenames[j+1])]='\0';
strcpy(filenames[j+1],filenames[j]);
filenames[j+1][strlen(filenames[j])]='\0';
strcpy(filenames[j],temp);
filenames[j][strlen(temp)]='\0';
}
}
for(i=0;i<count;i++)
{
display(flag_param,filenames[i]);//根据命令行参数和完整路径显示目标文件
}
closedir(dir);
//如果命令行中没有-l选项,打印一个换行符
if((flag_param & LS_L)==0)
printf("\n");
}
}
void displayR_attribute(char* name) //当l和R都有时,先调用display_attribute打印,然后该函数负责递归
{
struct stat buf;
if(lstat(name,&buf)==-1)
{
my_err("stat",__LINE__);
}
if(S_ISDIR(buf.st_mode))
{
display_single(name);
free(name);
char* p=PATH;
while(*++p);
while(*--p!='/');
*p='\0'; //每次递归完成之后将原来的路径回退至递归前
chdir(".."); //跳转到当前目录的上一层目录
return;
}
}