实现ls命令(带有-a和-l参数)de过程总结
代码块
开心的贴代码时间
@galaxyxupt
#include<sys/types.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#include<sys/stat.h>
#include<unistd.h>
#include<dirent.h>
#include<grp.h>
#include<pwd.h>
#include<errno.h>
//#include<Linux/limits.h>
--- 这个头文件被注释掉了一是因为我的ubuntu上找不到,二是貌似注释掉了也没什么影响
--- 还有就是本代码大部分借鉴于《Linux C 编程实战》第六章的编程实践部分,有部分改动
#define MAXROWLEN 150 //设置一行所能打印的字数
int g_leave_len = MAXROWLEN; //行剩余可用字数,最开始肯定为最大值,也就是行能容纳的字数
int g_maxlen; //获取的文件名中最长文件名的字数
void myerr(char *errstr,int line); //自定义的错误处理函数声明
void display_att(struct stat buf,char *name) //打印文件属性的函数
{
char buf_time[50];
struct passwd *psd;
struct group * grp; //以上两个结构体包含在头文件中,无需自己定义
if(S_ISLNK(buf.st_mode)) //判断是否为链接文件,ISLNK应该就是is link的意思,下面的自己推啦
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");
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("-");
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("-");
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(" ");//
psd = getpwuid(buf.st_uid);//终于看见汉字了,四不四很激动,上面那一串都差不多,大家自行理解就好,嘿嘿
grp = getgrgid(buf.st_gid);//这两个函数可以分别通过uid和gid获取用户名和用户组名,返回一个结构体,至于为啥不直接返回字符指针我也想知道
printf("%4ld",buf.st_nlink);//。。。这个buf可神奇了,里面啥都有。。。st_nlink是该文件的硬连接数
printf("%-8s",psd->pw_name);//pw_name是用户名,可能是因为用户名在passwd里面吧
printf("%-8s",grp->gr_name);//group这个单词认识吧,组名
printf("%6ld",buf.st_size);//文件大小
strcpy(buf_time,ctime(&buf.st_mtime));//获取最后一次被修改的时间时间。st_atime:最近一次被访问的时间。st_ctime:最近一次被更改的时间,
buf_time[strlen(buf_time)-1] = '\0';//这个语句开始我是并不在意的,直到。。。打印出来的文件名全都换了行,开始还以为是行最大字数不够大,最后才知道ctime函数的返回值自带换行符。。。这个语句用'\0'覆盖掉'\n'。
printf(" %s",buf_time);//打印时间
}
void display_sin(char *name)//打印单个文件名
{
int i,len;
if(g_leave_len < g_maxlen)//全局变量
{
printf("\n");//如果行剩余字数小于最大文件名的字数就换行,因为打印文件名的时候是按照最大文件名对齐的
g_leave_len = MAXROWLEN;//换到下行后行剩余字数刷新为行最大字数
}
len = strlen(name);
len = g_maxlen - len;//len的最终目的是要等于该文件名字数与最大文件名字数的差值,以便补上相应数量的空格使之对齐
printf("%-s",name);//打印文件名
for(i=0;i<len;i++)
printf(" ");补齐空格
printf(" ");//这个空格是每个文件名之间的间隔,设置为两个空格
g_leave_len -= (g_maxlen+2);//每打印一次会消耗 最大文件名字数+2 个空格
}
void display(int flag,char * filename)//flag后面再讲,filename是绝对路径
{
int i,j;
char name[50];
struct stat buf;//头文件自带结构体,存储文件的各种属性
//从路径中截取文件名
for(i=0,j=0;i<strlen(filename);i++)
{
if(filename[i]=='/')
{
j=0;
continue;
}
name[j++] = filename[i];
}
name[j] = '\0';
//上面这个就设计的很巧妙,这样循环,最后只能保存下来最后一个/后面的字符,也就是我们需要的文件名
if(stat(filename,&buf)==-1)
perror("lstat");
//1:l 2:a 3:al or la 0:only 接受到的-l参数记作1,以此类推,感觉这样比书上的简单,0的话就说明没有接受到参数
if(flag==0)//没有参数
{ if(name[0]!='.')
display_sin(name);//打印除了隐藏文件的文件名
}
else if(flag==1)//-l
{ if(name[0]!='.')
{
display_att(buf,name);
printf(" %-s\n",name);//打印除隐藏文件外的文件详细信息
}
}
else if(flag==2)//-a
display_sin(name);//打印全部文件名
else if(flag==3)//-al or -la or -l -a
{
display_att(buf,name);
printf(" %-s\n",name);//打印所有文件的详细信息
}
}
void display_dir(int flag_param,char *path)//打印目录
{
DIR *dir;
int count=0;
char filenames[256][50];//用二维数组保存文件名
char tmp[50];
struct dirent *ptr;
//获取指定目录下文件总数和最长文件名长度
dir = opendir(path);
if(dir==NULL)
myerr("opendir",__LINE__);
while((ptr=readdir(dir))!=NULL)
{
if(g_maxlen<strlen(ptr->d_name))
g_maxlen = strlen(ptr->d_name);//使g_maxlen始终保持最大
count++;//计文件数
}
closedir(dir);
if(count>256)
myerr("files is too many",__LINE__);
int len = strlen(path);
dir = opendir(path);
for(int i=0;i<count;i++)
{
ptr = readdir(dir);
if(ptr==NULL)
myerr("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';//最后补'\0'
}
//冒泡对文件名排序,没啥说的,就是注意补'\0'
for(int i=0;i<count;i++)
for(int j=0;j<count-1;j++)
{
if(strcmp(filenames[j],filenames[j+1])>0)
{
strcpy(tmp,filenames[j]);
tmp[strlen(filenames[j])] = '\0';
strcpy(filenames[j],filenames[j+1]);
filenames[j][strlen(filenames[j+1])] = '\0';
strcpy(filenames[j+1],tmp);
filenames[j+1][strlen(tmp)] = '\0';
}
}
for(int i=0;i<count;i++)
display(flag_param,filenames[i]);
closedir(dir);
}
void myerr(char *errstr,int line)
{
fprintf(stderr,"line:%d",line);
perror(errstr);
exit(1);
}
//终于开始的主函数。。。
int main(int argc,char** argv)
{
char path[100] = "./";
char param[20];
struct stat buf;
int flag=0,flag2=0;
for(int i=1;i<argc;i++)
{
if(argv[i][0]=='-')
{
for(int j=1;j<1+strlen(argv[i]);j++)
{
if(argv[i][j]=='l')
{
if(flag==2)//判断是否已经接收到-a,如果接收到,则设置flag=3,下面的同理
flag = 3;
else if(flag==0)//说明没有接收到其他参数
flag = 1;
}
if(argv[i][j]=='a')
{
if(flag==1)
flag = 3;
else if(flag==0)
flag = 2;
}
}
}
else
{
strcpy(path,argv[i]);
path[strlen(argv[i])] = '\0';//注意补'\0'
flag2 = 1;
}
}
if(flag2==0)//flag2为零,说明没有接受到参数,则直接打印当前目录
display_dir(flag,path);
if(stat(path,&buf)==-1)//无效路径
myerr("stat",__LINE__);
//接下来判断是目录文件还是其他文件
if(S_ISDIR(buf.st_mode))
{
if(path[strlen(path)-1]!='/')
{
path[strlen(path)] = '/';
path[strlen(path)+1] = '\0';
}
else
path[strlen(path)] = '\0';//确保路径名以/结尾
display_dir(flag,path);
}
else
{
display(flag,path);
}
}