首先引用维基百科对ls的定义:ls命令在类unix操作系统中被用于显示文件夹中的内容。ls即list,英文表示为"list directory contents"。
当我们使用Linux时不可避免的使用ls命令,通过自己的敲码,学长的帮助与网上的翻阅资料,本文通过C语言粗略的实现ls的部分功能。如有不足,欢迎指出。.^_^.
目标实现内容:
- 实现 ls 的 -a、-l、-R、-t、-r、-i、-s 参数,并允许这些参数任意组合.
-R
需要通对/
的遍历测试- 界面美观(输出对齐、与颜色显示等)
- 无资源与内存泄露
首先,要实现ls的功能,首先要知道我们要实现的功能有哪些.
- a 显示所有文件及目录 (. 开头的隐藏文件也会列出)
- -l 除文件名称外,亦将文件型态、权限、拥有者、文件大小等资讯详细列出
- -r 将文件以相反次序显示(原定依英文字母次序)
- -t 将文件依建立时间之先后次序列出
- -R 若目录下有文件,则以下之文件亦皆依序列出
- -i 查看文件的inode号(inode存储文件的详细信息)
- -s 列出文件,并显示文件大小
其次,要对自己的my_ls的整体有清晰的而紧密链接的脉络,不然等自己写好再调试的时候会很痛苦。(像我一样)。
话不多说,让我们来看下具体的思路。
目录
三. 在my_ls这个程序中有三个主心骨,怎么说,连接着整个程序的运行的三个函数。
负一:argc和argv.
对于从命令行获取参数我们的mani()要变成 int main(int argc ,char**argv);
int main(int argc,char*argv[])
argc 是 argument count的缩写,表示传入main函数的参数个数;
argv表示传入main函数的参数序列或指针,并且第一个参数argv[0]一定是程序的名称,并且包含了程序所在的完整路径,所以确切的说需要我们输入的main函数的参数个数应该是argc-1个;argv是指向指针的指针,main函数的第二个参数“char *argv[]“也可以替换为 “char **argv“,两者是等价的。
比如说:
./my_ls -i /home
我们运行my_ls 这个程序, argc 是3 ,argv[0]=./my_ls ,argv[1]=-i,argv[2]=/home;
零,一些必要的函数与头文件。
(1)stat和lstat用以获取文件信息。
头文件:
include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
函数原型:int stat(const char *path, struct stat *buf)
返回值:成功返回0,失败返回-1;
参数:文件路径(名),struct stat 类型的结构体
struct stat 结构体详解:
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 */ 最后一次改变该文件状态的时间
};
stat和lstat的功能基本相同,不过lstat不会对目标文件进行解引用(所以我更推荐lstat,以免发生什么奇怪的反应)。
2.opendir,closedir,readdir,chdir;
相信大家对前三个打开,关闭,读取目录比较熟悉,我这里介绍一下 chdir(在后面实现递归和引用的参数中有指定路径时特别重要)
函数名: chdir改变当前工作目录到path.
头文件: #include <dir.h>
int chdir(const char *path);
其余一些我在代码中将会注释。
一.对传入的参数怎么标记(太妙了,我自己是真没想到)
对于 ls命令的参数要求能任意组合比如 -i 或者 -is 或者 -i -s 。假如我们按正常思路那我们可能要开一个数组来判断传入了什么参数,以及参数的各种组合。这样就略显麻烦了。实际上我们可以通过二进制 来标记。 通过一个int型的flag,对2的倍数进行 与 | 操作。类似这样:
#define PARAM_a 1
#define PARAM_l 2
#define PARAM_R 4
#define PARAM_t 8
#define PARAM_r 16
#define PARAM_i 32
#define PARAM_s 64
for(int i=1;i<argc;i++)
{
if(argv[i][0]=='-')
{
for(int j=1;j<strlen(argv[i]);j++)
{
param[cnt++]=argv[i][j];
}
num++;
}
}
//通过flag标记参数
for(int k=0;k<cnt;k++)
{
if(param[k]=='a')
flag|=PARAM_a;
else if(param[k]=='l')
flag|=PARAM_l;
else if(param[k]=='i')
flag|=PARAM_i;
else if(param[k]=='R')
flag|=PARAM_R;
else if(param[k]=='r')
flag|=PARAM_r;
else if(param[k]=='t')
flag|=PARAM_t;
else if(param[k]=='s')
flag|=PARAM_s;
}
注意 ‘-’后面就跟着ls的参数。
同样的对于后面的命令行参数的组合时,也只要判断flag等于哪些参数之和就行了。
ps: 学长说可以通过getpot()函数直接获取参数,还快捷方便。
二. 彩色输出文件名。
格式:
printf("\033[字背景颜色;字体颜色m字符串\033[0m" );
字背景颜色: 40–49 | 字颜色: 30–39 |
---|---|
40: 黑 | 30: 黑 |
41: 红 | 31: 红 |
42: 绿 | 32: 绿 |
43: 黄 | 33: 黄 |
44: 蓝 | 34: 蓝 |
45: 紫 | 35: 紫 |
46: 深绿 | 36: 深绿 |
47:白色 | 37:白色 |
三. 在my_ls这个程序中有三个主心骨,怎么说,连接着整个程序的运行的三个函数。
display_dir,display_file,display_single.
1.display_dir,当判断为目录时使用。
2.display_file,当判断为文件时使用。
3.display_single彩色输出文件名。
具体的内容请看代码。
四.输出对齐(这个太坑了)
首先 你要 设几个全局变量
g_max_len 最长文件名 (在display_dir遍历文件时能得到)
g_leave_len 本行剩下的格数。 初始为 MAXROWLEN=155;
h_max 本行最多能输出几个文件名
拿 display_single举例:
void display_single(char *name,int filecolor)
{
char colorname[NAME_MAX + 30];
int i,len,j = 0;
len = strlen(name);
h++;
if(g_leave_len<=g_maxlen||h==h_max)
{
printf("\n");
g_leave_len=MAXROWLEN;
h=0;
}
sprintf(colorname,"\033[%dm%s\033[0m",filecolor,name);
printf(" %-s",colorname);
for(int i=0;i<len;i++)
{
if(name[i]<0)
j++;
}
len=g_maxlen-len+j/3; //
g_leave_len-=(g_maxlen+10);
for(int i=0;i<len+6;i++)
printf(" ");
}
这边倒数第二个的循环中,为什么要判断name[i]是否小于0呢? 因为文件名里可能有字母啊》》。
而通过strlen()获取字符串长度时,遇到汉字会返回三个字节,所以后面要j/3.
至于h_max,一行最多能放下几个文件名则通过 h_max=g_leave_len/(g_maxlen+15)获取(学长说是一个公式我也不太了解)。
五.对与一些参数可以在flag之前就先处理掉。
后面就不用再flag |与了,在后面参数组合时也能够减轻工作量。
比如-t 按最后修改时间排序,我写了一个time_qsort但是不知道为什么不能在大一点的工作目录下运行,最后还是普通地用了插入排序。
-a则是混入到各个命令参数之间, 比如 -ia 和-i的区别就是多一个判断语句:
else if(flag==(PARAM_i+PARAM_a))
{
ls_i(name,filecolor);
}
else if (flag==PARAM_i)
{
if(name[0]!='.')
ls_i(name,filecolor);
}
五.各个参数的实现中比较难的可能就是-R了,
我一开始写的程序中对于普通的目录能够进行遍历,但是一到大点的目录时就会提示 segament fault 段错误,因为一开始我用的是普通的二维数组,测试用例大点就爆了,通过网上查询提示可以用动态数组或者链表,我下面的实现是通过动态数组实现的。
后面遍历 / 目录时又出错了 ,提示我 Permission denied 没有权限 ,后面发现在进行-R有关命令之前需要进入超级用户root模式,在运行程序之前(以Ubuntu为例)
su root
//输入你的密码
之后没有出现上述两个错误了,可是。。。 后面又提示出现了在碰到 /proc的某个文件时提示(自己写的my_error()错命令的作用,可惜不会gdb,Printf大法麻了)没有这个文件no such file or directory ,实在找不到办法我就只能当读取到/proc的某个文件报错后跳过这个文件了。。。
六. code
下面是代码:
#include<stdio.h>
#include <linux/limits.h> //包含PATH_MAX =260
#include<string.h>
#include<sys/stat.h> //提供 stat函数
#include<sys/types.h>
#include<stdlib.h>
#include<pwd.h> //找uid
#include<grp.h> //找gid
#include<time.h> //转换时间
#include<unistd.h>
#include<dirent.h>
#include<errno.h> //错误的代码会传到errno中
#define PARAM_NONE 0 //通过二进制 | 来标记flag
#define PARAM_a 1
#define PARAM_l 2
#define PARAM_R 4
#define PARAM_t 8
#define PARAM_r 16
#define PARAM_i 32
#define PARAM_s 64
#define MAXROWLEN 155
int h=0,h_max=2;
int g_maxlen; //这个目录下最长的文件名
int g_leave_len = MAXROWLEN; //本行剩余长度
void my_error(const char * err_string,int line);
void display_single(char *name ,int color);
void ls_l(struct stat buf,char *name,int color);
void ls_i(char *name,int color);
void ls_s(char * name,int color);
void ls_R(char *path,int flag);
void display_dir(int flag,char*path);
void display_file(int flag,char *filename);
int cmp(const void *_a,const void *_b);
int main(int argc,char*argv[])
{
char path[PATH_MAX+1]; //写入路径
int flag; //判断参数。
struct stat buf;
int cnt=0,num=0;
char param[100];
flag=PARAM_NONE;
for(int i=1;i<argc;i++)
{
if(argv[i][0]=='-')
{
for(int j=1;j<strlen(argv[i]);j++)
{
param[cnt++]=argv[i][j];
}
num++;
}
}
//通过flag标记参数
for(int k=0;k<cnt;k++)
{
if(param[k]=='a')
flag|=PARAM_a;
else if(param[k]=='l')
flag|=PARAM_l;
else if(param[k]=='i')
flag|=PARAM_i;
else if(param[k]=='R')
flag|=PARAM_R;
else if(param[k]=='r')
flag|=PARAM_r;
else if(param[k]=='t')
flag|=PARAM_t;
else if(param[k]=='s')
flag|=PARAM_s;
}
if(num+1==argc) //说明命令中没有路径,所以默认路径为./当前目录下
{ //printf("1\n");
strcpy(path,"./");
path[2]='\0';
display_dir(flag,path);
return 0;
}
for(int m=1;m<argc;m++)
{
if(argv[m][0]=='-')
{
continue;
}
else
{
if(lstat(argv[m],&buf)==-1)
{
my_error("lstat",__LINE__);
}
if(S_ISDIR(buf.st_mode)) //是不是目录
{ strcpy(path,argv[m]);
if(path[strlen(argv[m])-1]!='/')
{
path[strlen(argv[m])]='/';
path[strlen(argv[m])+1]='\0';
}
else
{
path[strlen(argv[m])]='\0';
}
printf("%s\n",path);
if(chdir(path)==-1)
{
my_error("chdir",__LINE__);
}
display_dir(flag,path);
}
else //是文件
{
strcpy(path,argv[m]);
display_file(flag,path);
}
printf("\n");
}
}
return 0;
}
void my_error(const char * err_string,int line) //报错函数
{
fprintf(stderr,"line:%d ",line);
perror(err_string);
exit(1);
}
int cmp(const void *_a,const void *_b)
{
char *a=(char*)_a;
char *b=(char*)_b;
return strcmp(a,b);
}
void display_single(char *name,int filecolor) //打印文件名
{
char colorname[NAME_MAX + 30];
int i,len,j = 0;
len = strlen(name);
h++;
if(g_leave_len<=g_maxlen||h==h_max)
{
printf("\n");
g_leave_len=MAXROWLEN;
h=0;
}
sprintf(colorname,"\033[%dm%s\033[0m",filecolor,name);
printf(" %-s",colorname);
for(int i=0;i<len;i++)
{
if(name[i]<0)
j++;
}
len=g_maxlen-len+j/3;
g_leave_len-=(g_maxlen+10);
for(int i=0;i<len+6;i++)
printf(" ");
}
void ls_l(struct stat buf,char *name,int color)
{
char colorname[PATH_MAX+1];
struct passwd *uid;
struct group *gid;
char mtime[100];
//文档类别
if(S_ISLNK(buf.st_mode))
printf("l");
else if(S_ISREG(buf.st_mode))
printf("-");
else if(S_ISDIR(buf.st_mode))
printf("d");
else if(S_ISCHR(buf.st_mode))
printf("c");
else if(S_ISBLK(buf.st_mode))
printf("b");
else if(S_ISFIFO(buf.st_mode))
printf("f");
else if(S_ISSOCK(buf.st_mode))
printf("s");
//user 权限
if(buf.st_mode&S_IRUSR)
printf("r");
else printf("-");
if(buf.st_mode&S_IWUSR)
printf("w");
else
printf("-");
if(buf.st_mode&S_IXUSR)
printf("x");
else
printf("-");
//group 权限
if(buf.st_mode&S_IRGRP)
printf("r");
else
printf("-");
if(buf.st_mode&S_IWGRP)
printf("w");
else
printf("-");
if(buf.st_mode&S_IXGRP)
printf("x");
else
printf("-");
//other 权限
if(buf.st_mode&S_IROTH)
printf("r");
else
printf("-");
if(buf.st_mode&S_IWOTH)
printf("w");
else
printf("-");
if(buf.st_mode&S_IXOTH)
printf("x");
else
printf("-");
//链接数
printf("%4lu ",buf.st_nlink);
//uid和gid
uid=getpwuid(buf.st_uid);
gid=getgrgid(buf.st_uid);
if(uid==NULL||gid==NULL)
{
printf("can't get id");
return ; // __LINE__正在编译的行号
}
printf("%-8s ",uid->pw_name);
printf("%-8s",gid->gr_name);
//文档容量大小
printf("%8ld ",buf.st_size);
//最后修改时间
strcpy(mtime,ctime(&buf.st_mtime));
mtime[strlen(mtime)-1]='\0'; //不然会换行
printf("%s",mtime);
sprintf(colorname,"\033[%dm%s\033[0m",color,name);
printf(" %-s\n",colorname);
}
void ls_i(char *name,int color)
{ struct stat buf;
if(lstat(name,&buf)==-1)
{
my_error("lstat",__LINE__);
}
int j=0,len=strlen(name);
char colorname[NAME_MAX+1];
h++;
g_leave_len-=(g_maxlen+10);
if(g_leave_len<=g_maxlen||h==h_max)
{
printf("\n");
g_leave_len=MAXROWLEN;
h=0;
}
printf("%7ld",buf.st_ino);
sprintf(colorname,"\033[%dm%s\033[0m",color,name);
printf(" %-s",colorname);
for(int i=0;i<len;i++)
{
if(name[i]<0)
j++;
}
len=g_maxlen-len+j/3;
for(int i=0;i<len+6;i++)
printf(" ");
}
void ls_s(char * name,int color)
{ struct stat buf;
if(lstat(name,&buf)==-1)
{
my_error("lstat",__LINE__);
}
int j=0,len=strlen(name);
char colorname[NAME_MAX+1];
g_leave_len-=(g_maxlen+12);
if(g_leave_len<=g_maxlen)
{
printf("\n");
g_leave_len=MAXROWLEN;
h=0;
}
printf("%3d",buf.st_blocks/2);
sprintf(colorname,"\033[%dm%s\033[0m",color,name);
printf(" %-s",colorname);
h++;
if(h==h_max)
{
printf("\n");
g_leave_len=MAXROWLEN;
h=0;
}
else
{
for(int i=0;i<len;i++)
{
if(name[i]<0)
j++;
}
len=g_maxlen-len+j/3;
for(int i=0;i<len+1;i++)
printf(" ");
}
}
void ls_R(char*name,int flag)
{
DIR * dir;
struct dirent *ptr;
int i,count = 0;
struct stat buf;
char name_dir[10000];
if(chdir(name)<0) //将输入的目录改为当前目录,下面操作的前提
{
if(lstat(name,&buf)==-1)
{ if(errno==13)
{ printf("Permission denied\n");
return;
}
else if(strncmp(name,"/proc",4)==0)
{ printf("this is a /proc file\n");
return;
}
else
my_error("lstat",__LINE__);
}
}
if(getcwd(name_dir,10000)<0){
my_error("getcwd",__LINE__); //获取当前目录的绝对路径(重要,下面的打开目录的操作需要这个路径,否则需要手动添加)
}
printf("%s:\n",name_dir);
dir = opendir(name_dir); //用新获得的路径打开目录
if(dir==NULL){
my_error("opendir",__LINE__);
}
g_maxlen=0;
while((ptr = readdir(dir))!=NULL){
if(g_maxlen<strlen(ptr->d_name))
g_maxlen = strlen(ptr->d_name);
count++;
}
closedir(dir);
//动态数组(用静态数组会爆)
char**filenames =(char**)malloc(count*sizeof(char*)); //要进行初始化
memset(filenames,0,sizeof(char*)*count);
for(i=0;i<count;i++){
filenames[i]=(char*)malloc(256*sizeof(char));
memset(filenames[i],0,sizeof(char)*256);
}
int j,len=strlen(name_dir);
dir = opendir(name_dir);
for(i=0;i<count;i++){
ptr = readdir(dir);
if(ptr == NULL){
my_error("readdir",__LINE__);
}
strcat(filenames[i],ptr->d_name);
}
for(i=0;i<count;i++)
display_file(flag,filenames[i]);
printf("\n");
//递归实现核心部分
for(i=0;i<count;i++){
if(lstat(filenames[i],&buf)==-1)
{ if(errno==13)
{ printf("Permission denied\n");
return;
}
else if(strncmp(filenames[i],"/proc",4)==0)
{ printf("this is a /proc file\n");
return;
}
else
my_error("lstat",__LINE__);
}
if(strcmp(filenames[i],"..")==0)
continue;
if(filenames[i][0]=='.')
continue;
if(S_ISDIR(buf.st_mode)){
h=0;
g_leave_len=MAXROWLEN;
ls_R(filenames[i],flag);
}
else if(!S_ISDIR(buf.st_mode))
{
continue;
}
chdir("../"); //处理完一个目录后返回上一层
}
for(i=0;i<count;i++)
{
free(filenames[i]);
}
free(filenames);
closedir(dir);
}
void time_quicksort(long filetime[],char filename[256][PATH_MAX+1],int begin,int end) //大点的目录没有实现??
{ char tmpname[PATH_MAX+1];
if(begin>=end)
return ;
int i=begin;
int j=end;
long t;
long tmp=filetime[begin];
while(i!=j)
{
while(filetime[j]<=tmp&&j>i)
j--;
while(filetime[i]>=tmp&&j>i)
i++;
if(j>i)
{ //换时间
t=filetime[i];
filetime[i]=filetime[j];
filetime[j]=t;
//换文件名
strcpy(tmpname,filename[i]);
strcpy(filename[i],filename[j]);
strcpy(filename[j],tmpname);
}
}
filetime[begin]=filetime[i];
filetime[i]=tmp;
strcpy(tmpname,filename[i]);
strcpy(filename[i],filename[begin]);
strcpy(filename[begin],tmpname);
time_quicksort(filetime,filename,0,i-1);
time_quicksort(filetime,filename,i+1,end);
}
//
void display_file(int flag,char *filename)
{
int i,j=0,h=0,filecolor=37;//白色
char name[PATH_MAX+1];
char lujin[PATH_MAX+1];
struct stat buf;
memset(name,'\0',PATH_MAX+1);
for(i=0;i<strlen(filename);i++)
{
if(filename[i]=='/')
{
j=0;
h=i;
continue;
}
name[j++]=filename[i];
}
name[j]='\0'; //别漏掉'\0';
//判断颜色
if(lstat(filename,&buf)==-1)
{ if(errno==13)
return;
else
my_error("lstat",__LINE__);
}
if(S_ISDIR(buf.st_mode))
{
filecolor=34;
}
else if(S_ISBLK(buf.st_mode)){
filecolor=36;
}
if(filecolor == 37&&
( (buf.st_mode & S_IXUSR)||
(buf.st_mode & S_IXGRP)||
(buf.st_mode & S_IXOTH) ) ){
filecolor=33;
}
//解析参数 //不用t,r,R,在之前就已经先判断了 //display_dir()时
if(flag==PARAM_NONE)
{ if(name[0]!='.')
{
display_single(name,filecolor);
}
}
else if(flag==PARAM_r)
{
if(name[0]!='.')
{
display_single(name,filecolor);
}
}
else if(flag==PARAM_l)
{
if(name[0]!='.')
{
ls_l(buf,name,filecolor);
}
}
else if(flag==PARAM_i)
{ if(name[0]!='.')
{
ls_i(name,filecolor);
}
}
else if(flag==PARAM_a)
{
display_single(name,filecolor);
}
else if(flag==PARAM_s)
{ if(name[0]!='.')
{
ls_s(name,filecolor);
}
}
else if(flag==(PARAM_i+PARAM_a))
{
ls_i(name,filecolor);
}
else if(flag==(PARAM_a+PARAM_l))
{
ls_l(buf,name,filecolor);
}
else if(flag==(PARAM_i+PARAM_s))
{ if(name[0]!='.')
{
h_max=g_leave_len/(g_maxlen+15);
printf("%7ld ",buf.st_ino);
ls_s(name,filecolor);
}
}
else if(flag==(PARAM_l+PARAM_s))
{
if(name[0]!='.')
{
printf("%3ld ",buf.st_blocks/2);
ls_l(buf,name,filecolor);
}
}
else if(flag==(PARAM_a+PARAM_s))
{
ls_s(name,filecolor);
}
else if(flag==(PARAM_i+PARAM_l))
{
if(name[0] != '.')
{
printf("%7ld ",buf.st_ino);
ls_l(buf,name,filecolor);
}
}
else if(flag==(PARAM_i+PARAM_s+PARAM_l))
{
if(name[0] != '.')
{
h_max=g_leave_len/(g_maxlen+15);
printf("%7ld ",buf.st_ino);
printf("%ld ",buf.st_blocks/2);
ls_l(buf,name,filecolor);
}
}
else if(flag==(PARAM_i+PARAM_a+PARAM_s))
{
h_max= g_leave_len/(g_maxlen+15);
printf("%7ld ",buf.st_ino);
ls_s(name,filecolor);
}
else if(flag==(PARAM_l+PARAM_s+PARAM_a))
{
printf("%3ld ",buf.st_blocks/2);
ls_l(buf,name,filecolor);
}
else if(flag==(PARAM_i+PARAM_s+PARAM_l))
{ if(name[0]!='.')
{
printf("%7ld ",buf.st_ino);
printf("%3ld ",buf.st_blocks);
ls_l(buf,name,filecolor);
}
}
else if(flag==(PARAM_i+PARAM_a+PARAM_l))
{
printf("%7ld ",buf.st_ino);
ls_l(buf,name,filecolor);
}
else if(flag==(PARAM_i+PARAM_a+PARAM_l+PARAM_s))
{
printf("%7ld ",buf.st_ino);
printf("%3ld ",buf.st_blocks/2);
ls_l(buf,name,filecolor);
}
}
void display_dir(int flag,char*path)
{
DIR *dir;
struct dirent * ptr;
int cnt=0; //计算文件个数
char filename[256][PATH_MAX+1];
long filetime[256];
char tmpfilename[PATH_MAX+1];
struct stat buf;
dir=opendir(path);
if(dir==NULL)
{
my_error("opendir",__LINE__);
}
while((ptr=readdir(dir))!=NULL)
{
int hanzi=0;
int nohanzi=0;
for(int i=0;i<strlen(ptr->d_name);i++)
{
if(ptr->d_name[i]<0)
hanzi++;
else
nohanzi++;
}
int len=hanzi*2+nohanzi;
if(g_maxlen<len)
g_maxlen=len;
cnt++;
}
closedir(dir);
if(cnt>256) //过多文件
printf("%d :too many files under this dir",__LINE__);
dir=opendir(path);
for(int i=0;i<cnt;i++)
{
if((ptr=readdir(dir))==NULL)
{
my_error("readdir",__LINE__);
}
strncpy(filename[i],path,strlen(path));
filename[i][strlen(path)]='\0';
strcat(filename[i],ptr->d_name);
filename[i][strlen(path)+strlen(ptr->d_name)]='\0';
//printf("%d %s\n",i,filename[i]);
}
closedir(dir);
if(flag&PARAM_t)
{
flag-=PARAM_t;
struct stat buf;
for(int i=0;i<cnt;i++)
{
if(lstat(filename[i],&buf)==-1)
{
my_error("lstat",__LINE__);
}
filetime[i]=buf.st_mtime;
}
for(int i=0;i<cnt;i++)
for(int j=i;j<cnt;j++)
if(filetime[j]>filetime[i])
{
long int t;
t=filetime[i];
filetime[i]=filetime[j];
filetime[j]=t;
}
}
else //用文件名首字母排序
{
qsort(filename,cnt,sizeof(filename[0]),cmp);
}
//for(int i=0;i<cnt;i++)
//printf("%d %s\n",i,filename[i]);
int total=0;
//计算总量
if(flag&PARAM_a) //包括隐藏文件
{
for(int i=0;i<cnt;i++)
{
if(lstat(filename[i],&buf)==-1)
{
my_error("lstat",__LINE__);
}
total=total+buf.st_blocks/2; //???
}
}
else
{
for(int i=0;i<cnt;i++)
{
if(lstat(filename[i],&buf)==-1)
{
my_error("lstat",__LINE__);
}
if(filename[i][2]!='.')
total=total+buf.st_blocks/2; //???
}
}
if((flag&PARAM_l||flag&PARAM_s))
{
printf("总用量 %d\n",total);
}
if(flag&PARAM_r)
{ flag-=PARAM_r;
if(flag&PARAM_R) //??
{ flag-=PARAM_R;
ls_R(path,flag);
}
else
{
for(int i=cnt-1;i>=0;i--)
{
display_file(flag,filename[i]);
}
}
}
else
{
if(flag&PARAM_R)
{ flag-=PARAM_R;
ls_R(path,flag);
}
else
{
for(int i=0;i<cnt;i++)
{
display_file(flag,filename[i]);
}
}
}
}
2022.4.28 更正:
现在已经实现全部功能,
1. 主要是对于 -R 时 ,,要注意判断对没有权限的文件判断并跳过。 通过errno 当它=13时,错误为Perrision denied ,即为没有权限,跳过。
2.并且对于文件名时不要用数组,要的话也用动态数组malloc 或者链表,同时主要合适时机malloc
3.以及更正了按文件名排序。
实现代码:
#include<stdio.h>
#include <linux/limits.h> //包含PATH_MAX =260
#include<string.h>
#include<sys/stat.h> //提供 stat函数
#include<sys/types.h>
#include<stdlib.h>
#include<pwd.h> //找uid
#include<grp.h> //找gid
#include<time.h> //转换时间
#include<unistd.h>
#include<dirent.h>
#include<errno.h>
#define PARAM_NONE 0 //通过二进制 | 来标记flag
#define PARAM_a 1
#define PARAM_l 2
#define PARAM_R 4
#define PARAM_t 8
#define PARAM_r 16
#define PARAM_i 32
#define PARAM_s 64
#define MAXROWLEN 155
int h=0,h_max=2; //h_max一行最多多少个文件名
int flag1=0;
int g_maxlen; //最长文件名长度
int g_leave_len = MAXROWLEN; //本行剩下多少空间
int flag=1;
void my_error(const char * err_string,int line);
void display_single(char *name ,int color);
void ls_l(struct stat buf,char *name,int color);
void ls_i(char *name,int color);
void ls_s(char * name,int color);
int ls_R(char *path,int flag);
void display_dir(int flag,char*path);
void display_file(int flag,char *filename);
int cmp(const void *_a,const void *_b);
int main(int argc,char*argv[])
{
char path[PATH_MAX+1]; //写入路径
int flag; //判断参数。
struct stat buf;
int cnt=0,num=0;
char param[100];
flag=PARAM_NONE;
for(int i=1;i<argc;i++)
{
if(argv[i][0]=='-')
{
for(int j=1;j<strlen(argv[i]);j++)
{
param[cnt++]=argv[i][j];
}
num++;
}
}
//通过flag标记参数
for(int k=0;k<cnt;k++)
{
if(param[k]=='a')
flag|=PARAM_a;
else if(param[k]=='l')
flag|=PARAM_l;
else if(param[k]=='i')
flag|=PARAM_i;
else if(param[k]=='R')
flag|=PARAM_R;
else if(param[k]=='r')
flag|=PARAM_r;
else if(param[k]=='t')
flag|=PARAM_t;
else if(param[k]=='s')
flag|=PARAM_s;
}
if(num+1==argc) //说明命令中没有路径,所以默认路径为./当前目录下
{ //printf("1\n");
strcpy(path,"./");
path[2]='\0';
display_dir(flag,path);
return 0;
}
for(int m=1;m<argc;m++)
{
if(argv[m][0]=='-')
{
continue;
}
else
{
if(lstat(argv[m],&buf)==-1)
{ if(errno==13)
{
printf("Perssion denied\n");
errno=0;
}
else
my_error("lstat",__LINE__);
}
if(S_ISDIR(buf.st_mode)) //是不是目录
{ strcpy(path,argv[m]);
if(path[strlen(argv[m])-1]!='/')
{
path[strlen(argv[m])]='/'; //目录以/结尾
path[strlen(argv[m])+1]='\0';
}
else
{
path[strlen(argv[m])]='\0';
}
printf("%s\n",path);
if(chdir(path)==-1) //更换到dath目录下
{
my_error("chdir",__LINE__);
}
display_dir(flag,path);
}
else //是文件
{
strcpy(path,argv[m]);
display_file(flag,path);
}
printf("\n");
}
}
return 0;
}
void my_error(const char * err_string,int line)
{
fprintf(stderr,"line:%d ",line);
perror(err_string);
exit(1);
}
int cmp(const void *_a,const void *_b)
{
char *a=(char*)_a;
char *b=(char*)_b;
return strcmp(a,b)>0;
}
void display_single(char *name,int filecolor)
{
char colorname[NAME_MAX + 30];
int i,len,j = 0;
len = strlen(name);
h++;
if(g_leave_len<=g_maxlen||h==h_max)
{
printf("\n");
g_leave_len=MAXROWLEN;
h=0;
}
sprintf(colorname,"\033[%dm%s\033[0m",filecolor,name);
printf(" %-s",colorname);
for(int i=0;i<len;i++)
{
if(name[i]<0)
j++;
}
len=g_maxlen-len+j/3;
g_leave_len-=(g_maxlen+10);
for(int i=0;i<len+6;i++)
printf(" ");
}
void ls_l(struct stat buf,char *name,int color)
{
char colorname[PATH_MAX+1];
struct passwd *uid;
struct group *gid;
char mtime[100];
//文档类别
if(S_ISLNK(buf.st_mode))
printf("l");
else if(S_ISREG(buf.st_mode))
printf("-");
else if(S_ISDIR(buf.st_mode))
printf("d");
else if(S_ISCHR(buf.st_mode))
printf("c");
else if(S_ISBLK(buf.st_mode))
printf("b");
else if(S_ISFIFO(buf.st_mode))
printf("f");
else if(S_ISSOCK(buf.st_mode))
printf("s");
//user 权限
if(buf.st_mode&S_IRUSR)
printf("r");
else printf("-");
if(buf.st_mode&S_IWUSR)
printf("w");
else
printf("-");
if(buf.st_mode&S_IXUSR)
printf("x");
else
printf("-");
//group 权限
if(buf.st_mode&S_IRGRP)
printf("r");
else
printf("-");
if(buf.st_mode&S_IWGRP)
printf("w");
else
printf("-");
if(buf.st_mode&S_IXGRP)
printf("x");
else
printf("-");
//other 权限
if(buf.st_mode&S_IROTH)
printf("r");
else
printf("-");
if(buf.st_mode&S_IWOTH)
printf("w");
else
printf("-");
if(buf.st_mode&S_IXOTH)
printf("x");
else
printf("-");
//链接数
printf("%4lu ",buf.st_nlink);
//uid和gid
uid=getpwuid(buf.st_uid);
gid=getgrgid(buf.st_uid);
if(uid==NULL||gid==NULL)
{
printf("can't get id");
return ; // __LINE__正在编译的行号
}
printf("%-8s ",uid->pw_name);
printf("%-8s",gid->gr_name);
//文档容量大小
printf("%8ld ",buf.st_size);
//最后修改时间
strcpy(mtime,ctime(&buf.st_mtime));
mtime[strlen(mtime)-1]='\0'; //不然会换行
printf("%s",mtime);
sprintf(colorname,"\033[%dm%s\033[0m",color,name);
printf(" %-s\n",colorname);
}
void ls_i(char *name,int color)
{ struct stat buf;
if(lstat(name,&buf)==-1)
{
my_error("lstat",__LINE__);
}
int j=0,len=strlen(name);
char colorname[NAME_MAX+1];
h++;
g_leave_len-=(g_maxlen+10);
if(g_leave_len<=g_maxlen||h==h_max)
{
printf("\n");
g_leave_len=MAXROWLEN;
h=0;
}
printf("%7ld",buf.st_ino);
sprintf(colorname,"\033[%dm%s\033[0m",color,name);
printf(" %-s",colorname);
for(int i=0;i<len;i++)
{
if(name[i]<0)
j++;
}
len=g_maxlen-len+j/3;
for(int i=0;i<len+6;i++)
printf(" ");
}
void ls_s(char * name,int color)
{ struct stat buf;
if(lstat(name,&buf)==-1)
{
my_error("lstat",__LINE__);
}
int j=0,len=strlen(name);
char colorname[NAME_MAX+1];
g_leave_len-=(g_maxlen+12);
if(g_leave_len<=g_maxlen)
{
printf("\n");
g_leave_len=MAXROWLEN;
h=0;
}
printf("%3d",buf.st_blocks/2);
sprintf(colorname,"\033[%dm%s\033[0m",color,name);
printf(" %-s",colorname);
h++;
if(h==h_max)
{
printf("\n");
g_leave_len=MAXROWLEN;
h=0;
}
else
{
for(int i=0;i<len;i++)
{
if(name[i]<0)
j++;
}
len=g_maxlen-len+j/3;
for(int i=0;i<len+1;i++)
printf(" ");
}
}
int ls_R(char*name,int flag)
{
DIR * dir;
struct dirent *ptr;
int i,count = 0;
struct stat buf;
char name_dir[1000];
if(chdir(name)<0) //将输入的目录改为当前目录,下面操作的前提
{
if(errno==13)
{ errno=0;
printf("%s/%s :",name_dir,name);
printf("no perssion\n");
}
else
my_error("chdir",__LINE__);
return -1;
}
if(getcwd(name_dir,1000)<0){
my_error("getcwd",__LINE__);
return 0; //获取当前目录的绝对路径(重要,下面的打开目录的操作需要这个路径,否则需要手动添加)
}
printf("%s:\n",name_dir);
dir = opendir(name_dir); //用新获得的路径打开目录
if(dir==NULL){
if(errno==13)
{ errno=0;
printf("Permission denied 1\n");
}
else
{
my_error("opendir",__LINE__);
}
return 0;
}
while((ptr = readdir(dir))!=NULL){
if(g_maxlen<strlen(ptr->d_name))
g_maxlen = strlen(ptr->d_name);
count++;
}
closedir(dir);
//动态数组
char**filenames =(char**)malloc((count)*sizeof(char*)); //要进行初始化
memset(filenames,0,sizeof(char*)*(count));
for(i=0;i<count;i++){
filenames[i]=(char*)malloc(1024*sizeof(char));
memset(filenames[i],0,sizeof(char)*1024);
}
int j,len=strlen(name_dir),h=0;
dir = opendir(name_dir);
for(i=0;i<count;i++){
ptr = readdir(dir);
if(ptr == NULL){
if(errno==13)
{ errno=0;
printf("perssioned error\n");
}
else
my_error("readdir",__LINE__);
continue;
}
strcat(filenames[h++],ptr->d_name); //这里要注意用之前的初始化
}
for(i=0;i<h;i++)
{
display_file(flag,filenames[i]);
}
printf("\n");
for(i=0;i<h;i++){
if(lstat(filenames[i],&buf)==-1)
{ //printf("%s/%s\n",name_dir,filenames[i]);
if(errno==13)
{ printf("%s/%s\n",name_dir,filenames[i]);
errno=0;
printf("perrsion denied\n");
}
free(filenames[i]);
//my_error("lstat",__LINE__);
continue;
}
else
{
if(strcmp(filenames[i],"..")==0)
{
free(filenames[i]);
continue;
}
else if(strcmp(filenames[i],".")==0)
{ free(filenames[i]);
continue;
}
else if(S_ISDIR(buf.st_mode)){
if(ls_R(filenames[i],flag)!=-1)
chdir("../");
free(filenames[i]);
}
else if(!S_ISDIR(buf.st_mode))
{
continue;
free(filenames[i]);
}
}
}
//处理完一个目录后返回上一层
free(filenames);
closedir(dir);
return 1; //在函数开始时打开,结束时关闭
}
void time_quicksort(long filetime[],char filename[256][PATH_MAX+1],int begin,int end)
{ char tmpname[PATH_MAX+1];
if(begin>=end)
return ;
int i=begin;
int j=end;
long t;
long tmp=filetime[begin];
while(i!=j)
{
while(filetime[j]<=tmp&&j>i)
j--;
while(filetime[i]>=tmp&&j>i)
i++;
if(j>i)
{ //换时间
t=filetime[i];
filetime[i]=filetime[j];
filetime[j]=t;
//换文件名
strcpy(tmpname,filename[i]);
strcpy(filename[i],filename[j]);
strcpy(filename[j],tmpname);
}
}
filetime[begin]=filetime[i];
filetime[i]=tmp;
strcpy(tmpname,filename[i]);
strcpy(filename[i],filename[begin]);
strcpy(filename[begin],tmpname);
time_quicksort(filetime,filename,0,i-1);
time_quicksort(filetime,filename,i+1,end);
}
//
void display_file(int flag,char *filename)
{
int i,j=0,h=0,filecolor=37;//白色
char name[PATH_MAX+1];
char lujin[PATH_MAX+1];
struct stat buf;
memset(name,'\0',PATH_MAX+1);
for(i=0;i<strlen(filename);i++)
{
if(filename[i]=='/')
{
j=0;
h=i;
continue;
}
name[j++]=filename[i];
}
name[j]='\0'; //别漏掉'\0';
//判断颜色
if(lstat(filename,&buf)==-1)
{
my_error("lstat",__LINE__);
}
if(S_ISDIR(buf.st_mode))
{
filecolor=34;
}
else if(S_ISBLK(buf.st_mode)){
filecolor=36;
}
if(filecolor == 37&&
( (buf.st_mode & S_IXUSR)||
(buf.st_mode & S_IXGRP)||
(buf.st_mode & S_IXOTH) ) ){
filecolor=33;
}
//解析参数 //不用t,r,R,在之前就已经先判断了 //display_dir()时
if(flag==PARAM_NONE)
{ if(name[0]!='.')
{
display_single(name,filecolor);
}
}
else if(flag==PARAM_r)
{
if(name[0]!='.')
{
display_single(name,filecolor);
}
}
else if(flag==PARAM_l)
{
if(name[0]!='.')
{
ls_l(buf,name,filecolor);
}
}
else if(flag==PARAM_i)
{ if(name[0]!='.')
{
ls_i(name,filecolor);
}
}
else if(flag==PARAM_a)
{
display_single(name,filecolor);
}
else if(flag==PARAM_s)
{ if(name[0]!='.')
{
ls_s(name,filecolor);
}
}
else if(flag==(PARAM_i+PARAM_a))
{
ls_i(name,filecolor);
}
else if(flag==(PARAM_a+PARAM_l))
{
ls_l(buf,name,filecolor);
}
else if(flag==(PARAM_i+PARAM_s))
{ if(name[0]!='.')
{
h_max=g_leave_len/(g_maxlen+15); //貌似是一个公式
printf("%7ld ",buf.st_ino);
ls_s(name,filecolor);
}
}
else if(flag==(PARAM_l+PARAM_s))
{
if(name[0]!='.')
{
printf("%3ld ",buf.st_blocks/2);
ls_l(buf,name,filecolor);
}
}
else if(flag==(PARAM_a+PARAM_s))
{
ls_s(name,filecolor);
}
else if(flag==(PARAM_i+PARAM_l))
{
if(name[0] != '.')
{
printf("%7ld ",buf.st_ino);
ls_l(buf,name,filecolor);
}
}
else if(flag==(PARAM_i+PARAM_s+PARAM_l))
{
if(name[0] != '.')
{
h_max=g_leave_len/(g_maxlen+15);
printf("%7ld ",buf.st_ino);
printf("%ld ",buf.st_blocks/2);
ls_l(buf,name,filecolor);
}
}
else if(flag==(PARAM_i+PARAM_a+PARAM_s))
{
h_max= g_leave_len/(g_maxlen+15);
printf("%7ld ",buf.st_ino);
ls_s(name,filecolor);
}
else if(flag==(PARAM_l+PARAM_s+PARAM_a))
{
printf("%3ld ",buf.st_blocks/2);
ls_l(buf,name,filecolor);
}
else if(flag==(PARAM_i+PARAM_s+PARAM_l))
{ if(name[0]!='.')
{
printf("%7ld ",buf.st_ino);
printf("%3ld ",buf.st_blocks);
ls_l(buf,name,filecolor);
}
}
else if(flag==(PARAM_i+PARAM_a+PARAM_l))
{
printf("%7ld ",buf.st_ino);
ls_l(buf,name,filecolor);
}
else if(flag==(PARAM_i+PARAM_a+PARAM_l+PARAM_s))
{
printf("%7ld ",buf.st_ino);
printf("%3ld ",buf.st_blocks/2);
ls_l(buf,name,filecolor);
}
}
void display_dir(int flag,char*path)
{
DIR *dir;
struct dirent * ptr;
int cnt=0; //计算文件个数
long filetime[256];
char tmpfilename[PATH_MAX+1];
struct stat buf;
dir=opendir(path);
if(dir==NULL)
{
my_error("opendir",__LINE__);
return;
}
while((ptr=readdir(dir))!=NULL)
{
int hanzi=0;
int nohanzi=0;
for(int i=0;i<strlen(ptr->d_name);i++)
{
if(ptr->d_name[i]<0)
hanzi++;
else
nohanzi++;
}
int len=hanzi*2+nohanzi;
if(g_maxlen<len)
g_maxlen=len;
cnt++;
}
closedir(dir);
char **filename=(char**)malloc(sizeof(char *)*(cnt+1));
for(int i=0;i<cnt+1;i++)
filename[i]=(char*)malloc(sizeof(char)*100);
dir=opendir(path);
for(int i=0;i<cnt;i++)
{
if((ptr=readdir(dir))==NULL)
{
my_error("readdir",__LINE__);
}
strncpy(filename[i],path,strlen(path));
filename[i][strlen(path)]='\0';
strcat(filename[i],ptr->d_name);
filename[i][strlen(path)+strlen(ptr->d_name)]='\0';
//printf("%d %s\n",i,filename[i]);
}
closedir(dir);
if(flag&PARAM_t)
{
flag-=PARAM_t;
struct stat buf;
for(int i=0;i<cnt;i++)
{
if(lstat(filename[i],&buf)==-1)
{
my_error("lstat",__LINE__);
}
filetime[i]=buf.st_mtime;
}
for(int i=0;i<cnt;i++)
for(int j=i;j<cnt;j++)
if(filetime[j]>filetime[i])
{
long int t;
t=filetime[i];
filetime[i]=filetime[j];
filetime[j]=t;
}
//time_quicksort(filetime,filename,0,cnt-1);
}
else //用文件名首字母排序
{
for(int i=0;i<cnt;i++)
{
for(int j=i+1;j<cnt;j++)
{
if(strcmp(filename[i],filename[j])>0)
{
char tmp[1024];
strcpy(tmp,filename[i]);
strcpy(filename[i],filename[j]);
strcpy(filename[j],tmp);
}
}
}
}
//for(int i=0;i<cnt;i++)
//printf("%d %s\n",i,filename[i]);
int total=0;
//计算总量??
if(flag&PARAM_a) //包括隐藏文件
{
for(int i=0;i<cnt;i++)
{
if(lstat(filename[i],&buf)==-1)
{
my_error("lstat",__LINE__);
}
total=total+buf.st_blocks/2; //???
}
}
else
{
for(int i=0;i<cnt;i++)
{
if(lstat(filename[i],&buf)==-1)
{
my_error("lstat",__LINE__);
}
if(filename[i][2]!='.')
total=total+buf.st_blocks/2; //???
}
}
if((flag&PARAM_l||flag&PARAM_s))
{
printf("总用量 %d\n",total);
}
if(flag&PARAM_r)
{
if(flag&PARAM_R)
{ flag-=PARAM_R;
ls_R(path,flag);
}
else
{ flag-=PARAM_r;
for(int i=cnt-1;i>=0;i--)
{
display_file(flag,filename[i]);
}
}
}
else
{
if(flag&PARAM_R)
{ flag-=PARAM_R;
ls_R(path,flag);
}
else
{
for(int i=0;i<cnt;i++)
{
display_file(flag,filename[i]);
}
}
}
for(int i=0;i<cnt+1;i++)
{
free(filename[i]);
}
free(filename);
}