目录
一、任务说明
ls
是 GNU/Linux
操作系统中常见的命令。请使用 C 语言,基于 GNU/Linux
操作系统,编程实现 ls
命令的 部分 功能,包括实现 ls 的 -a、-l、-R、-t、-r、-i、-s 参数。
二、命令解析
-a | 列出目录下的所有文件,包括以.开头的隐含文件 |
-l | 列出文件的详细信息(包括文件属性和权限等) |
-R | 使用递归连同目录中的子目录中的文件显示出来,如果要显示隐藏文件就要添加-a参数(列出所有子目录下的文件) |
-t | 按修改时间进行排序,先显示最后编辑的文件 |
-r | 对目录反向排序(以目录的每个首字符所对应的ASCII值进行大到小排序) |
-i | 输出文件的i节点的索引信息 |
-s | 在每个文件名后输出该文件的大小 |
三、命令终端效果展示
-a--->列出目录下的所有文件
-l+文件名--->列出文件的详细信息
-R--->列出目录中子目录下的所有文件
(目录子目录文件过多,在此不进行全部展示)
-t--->按修改时间,由近到远进行排序
-r--->以目录的每个首字符所对应的ASCII值进行大到小排序
-i--->输出文件的i节点的索引信息
-s--->在每个文件名后输出该文件的大小
四、大致实现思路
- 先通过终端调用命令后,观察其输出内容的格式;
- 然后逐个分析实现参数,分块完成;
- 每个参数在整体实现中用自定义函数的形式独立完成,结合作用函数。
五、棘手点
- 整套ls实现的构造框架和设计
- -R需要对根目录进行查询,且需要用到递归
- 细节点很多,需要处处注意
- 参数与参数之间的优先级需要好好考虑,写嵌套时顺序不要出错
六、一些小点
1、文件名的获取
可以使用函数opendir和readdir打开文件,使用完后需要用closedir关闭:
(1)opendir函数
头文件 | #include <sys/types.h> #include <dirent.h> |
函数 | DIR *opendir(const char *name) |
含义 | 用来打开参数name 指定的目录, 并返回DIR*形态的目录流, 和open()类似, 接下来对目录的读取和搜索都要使用此返回值. |
其中DIR这一结构体的定义:
struct __dirstream
{
void *__fd;
char *__data;
int __entry_data;
char *__ptr;
int __entry_ptr;
size_t __allocation;
size_t __size;
__libc_lock_define (, __lock)
};
typedef struct __dirstream DIR;
函数 DIR *opendir(const char *pathname),即打开文件目录,返回的就是指向DIR结构体的指针,而该指针由以下几个函数使用:
struct dirent *readdir(DIR *dp);
void rewinddir(DIR *dp);
int closedir(DIR *dp);
long telldir(DIR *dp);
void seekdir(DIR *dp,long loc);
(2)readdir函数
头文件 | #include<sys/types.h> #include <dirent.h> |
函数 | struct dirent *readdir(DIR *dir); |
含义 | 返回参数dir 目录流的下个目录进入点 |
dirent这一结构体的定义:
struct dirent
{
ino_t d_ino; //d_ino 此目录进入点的inode
ff_t d_off; //d_off 目录文件开头至此目录进入点的位移
signed short int d_reclen; //d_reclen _name 的长度, 不包含NULL 字符
unsigned char d_type; //d_type 所指的文件类型
har d_name[256]; //d_name 文件名
};
(3)closedir函数
头文件 | #include<sys/types.h> #include <dirent.h> |
函数 | closedir(DIR *dir); |
含义 | 关闭dir流 |
2、stat函数
函数原型:int stat(const char *path, struct stat *buf);
其中path为文件名,struct stat * buf是一个保存文件状态信息的结构体,其类型如下:
struct stat{
dev_t st_dev; //文件的设备编号
ino_t st_ino; //文件的i-node(i节点编号)
mode_t st_mode; //文件的类型和存取权限,它的含义与chmod,open函数的mode参数相同
nlink_t st_nlink; //连到该文件的硬件链接数目,刚建立的文件值为1.
uid_t st_uid; //文件所有者的用户ID
gid_t st_gid; //文件所有组的组ID
dev_t st_rdev; //若此文件为设备文件,则为其设备编号
off_t st_size; //文件大小,以字节计算,对符号链接,该大小是其所指向文件名的长度
blksize_t st_blksize; //文件系统的I/O缓存区大小
blkcnt_t st_blocks; //占用文件区块的个数,每一区块大小通常为512个字节
time_t st_atime; //文件最后一次被访问的时间
time_t st_mtime; //文件最后一次被修改的时间,一般只能调用utime和write函数时才会变化
time_t st_ctime; //文件最近一次被修改的时间,此参数在文件所有者、所属组、文件权限被更改时更新
}
结构体的成员较多,但有的很少使用
常用的有:st_mode、st_uid、st_gid、st_size、st_atime、st_mtime
对于st_mode包含的文件类型信息,POSIX标准定义了一系列的宏:
S_ISLNK(st_mode): 判断是否为符号链接
S_ISREG(st_mode):判断是否为一般文件
S_ISDIR(st_mode):判断是否为目录文件
S_ISCHR(st_mode):判断是否为字符设备文件
S_ISBLK(st_mode):判断是否为快设备文件
S_ISFIFO(st_mode):判断是否为先进先出FIFO
S_ISSOCK(st_mode):判断是否为socketP
七、参考书籍
Linux-UNIX系统编程手册(上、下册) by Michael Kerrisk
八、代码展示part
/*
***-a 列出目录下的所有文件,包括以.开头的隐含文件
***-l 列出文件的详细信息(包括文件属性和权限等)
***-R 使用递归连同目录中的子目录中的文件显示出来,如果要显示隐藏文件就要添加-a参数
(列出所有子目录下的文件)
-t 按修改时间进行排序,先显示最后编辑的文件
-r 对目录反向排序(以目录的每个首字符所对应的ASCII值进行大到小排序)
***-i 输出文件的i节点的索引信息
-s 在每个文件名后输出该文件的大小
*/
/*
struct stat name
{
dev_t st_dev; //文件的设备编号
int_t st_ino; //节点
mode_t st_mode; //文件的类型和存取的权限
nlink_t st_nlink; //连到该文件的硬连接数目,刚建立的文件值为1
uid_t st_uid; //用户ID
gid_t st_gid; //组ID
dev_t st_rdev; //(设备类型)若此文件为设备文件,则为其设备编号
offf_t st_size; //文件字节数(文件大小)
unsigned long st_bilsize; //块大小(文件系统的I/O缓存区大小)
unsigend long st_blocks; //块数
time_t st_atime; //最后一次访问时间
time_t st_mtime; //最后一次修改时间
time_t st_ctime; //最后一次改变时间(指属性)
}
*/
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<pwd.h>
#include<grp.h>
#include<time.h>
#include<dirent.h>
#include<glob.h>
#include<stdlib.h>
#include<stdbool.h>
#define A 0
#define L 1
#define R 2
#define I 3
#define S 4
#define T 5
#define BULE 34
#define GREEN 32
struct file
{
char filename[256];
struct stat STA;
};
void LS_I(struct stat *STA);
void LS_S(struct stat *STA);
void LS_T(struct file *STA,int len);
void MODE(int mode, char str[]);
char* UID(uid_t uid);
char* GID(gid_t gid);
void show_file(struct stat* STA);
//获取当前路径打印文件名
void list(bool* name,int size)
{
DIR *dir =NULL;
struct dirent *ptr;
char ch = '0';
char buf[32] = {0};
char pathname_buf[256];
getcwd(pathname_buf,sizeof(pathname_buf)); //获取当前路径
dir=opendir(pathname_buf); //返回目录流
if(dir == NULL) //如果路径为空,返回错误
{
printf("error!cannot open the file\n");
exit(-1);
}
struct stat s_buf;
int stat_buf;
int i=0,j=0;
//buf->st_mtime 最后一次修改时间
struct file* buff = malloc(sizeof(struct file)*100);
struct file* temp = buff; //还原
while((ptr = readdir(dir))!= NULL)
{
if(name[A] == false)
{
if(ptr->d_name[0] == '.')
{
continue;
}
}
stat_buf=stat(ptr->d_name, &(buff->STA));
strcpy(buff->filename,ptr->d_name);
//printf("%s ", buff->filename);
buff++; //指针后移
j++; //记录文件数
}
buff = temp; //指向开始
if(name[T] == true)
{
LS_T(buff, j);
}
for(i=0;i<j;i++)
{
if(name[S] == true)
{
LS_S(&(buff+i)->STA);
}
if(name[I] == true)
{
LS_I(&(buff+i)->STA);
}
if(name[L] == true)
{
printf("\n");
show_file(&(buff+i)->STA);
}
//输出
if(S_ISDIR(buff->STA.st_mode))
{
//目录
COLOR(BULE);
printf("%5s",(buff+i)->filename);
}else if(buff->STA.st_mode & S_IXGRP)
{
//可执行文件(位运算?)
printf("....");
COLOR(GREEN);
printf("%5s",(buff+i)->filename);
}else if(S_ISREG(buff->STA.st_mode))
{
//普通文件
printf("......................");
printf(" %5s",(buff+i)->filename);
}
//printf(" %5s",(buff+i)->filename); //输出文件名
if(i % 5 ==0 && name[L] != true)
{
printf("\n");
}
}
if(name[R] == true) //-R递归
{
int i=0;
printf("\n");
for(;i<j;i++)
{
printf("%s:\n",(buff+i)->filename);
list(&(name[I]),3);
}
}
printf("\n");
closedir(dir);
}
/*
字颜色:
普通文件白色
32:绿色(可执行文件)
34:蓝色(目录文件):函数S_ISDIR()判断
*/
void COLOR(int color)
{
printf("\033[%dm",color);
}
// 展示单个文件的详细信息 -l
void show_file(struct stat* STA)
{
char modestr[11]; //存放权限
//权限
MODE((int)STA->st_mode, modestr);
//连到该文件的硬连接数目,刚建立的文件值为1
printf(" %5d", (int) STA->st_nlink);
//用户
printf(" %5s", UID(STA->st_uid));
//用户组
printf(" %5s", GID(STA->st_gid));
//文件大小
printf(" %5ld", (long) STA->st_size);
//ctime:最后一次改变文件内容或目录内容的时间
char buf_time[32];
strcpy(buf_time, ctime(&(STA->st_mtime)));
buf_time[strlen(buf_time) - 1] = '\0';
printf(" %5s",buf_time);
//文件名字
//printf(" %20s\n", filename);
}
//文件权限
void MODE(int mode, char str[])
{
strcpy(str, "----------"); //初始化全为----------
if(S_ISDIR(mode)) //是否为目录
{
str[0] = 'd';
}
if(S_ISCHR(mode)) //是否为字符设置
{
str[0] = 'c';
}
if(S_ISBLK(mode)) //是否为块设备
{
str[0] = 'b';
}
//逻辑与
/*
S_IRUSR:用户读权限
S_IWUSR:用户写权限
S_IRGRP:用户组读权限
S_IWGRP:用户组写权限
S_IROTH:其他组都权限
S_IWOTH:其他组写权限
*/
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';
}
printf("%s ",str);
}
//通过uid和gid找到用户名字和用户组名字
char* UID(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;
}
}
char* GID(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;
}
}
//打印i节点 -i
void LS_I(struct stat *STA)
{
printf("%ld ", STA->st_ino);
}
//文件大小 -s
void LS_S(struct stat *STA)
{
printf("%ld ",( long )STA->st_size);
}
//-t 进行排序
void LS_T(struct file *FILE,int len)
{
for(int i=1;i<=len-1;i++)
{
for(int j=1;j<len-i;j++)
{
if(FILE[i].STA.st_mtime<FILE[j].STA.st_mtime)//char* a; a-> = (*a)
{
struct file n = FILE[i];
FILE[i] = FILE[j];
FILE[j] = n;
}
}
}
}
int main(int argc,char** argv)
{
// ./ls5 -xxxxx
int i;
int command[10]={0};
int num=strlen(argv[1]);
printf("%s %s",argv[0],argv[1]);
for(i=0;i<num;i++)
{
if(argv[1][i]=='a')
{
command[A]++;
}else if(argv[1][i]=='l')
{
command[L]++;
}else if(argv[1][i]=='R')
{
command[R]++;
}else if(argv[1][i]=='i')
{
command[I]++;
}else if(argv[1][i]=='s')
{
command[S]++;
}else if(argv[1][i]=='t')
{
command[T]++;
}else{
printf("错误!\n");
return -1;
}
}
/*for(i=0;i<6;i++)
{
printf("\n%d ",command[i]);
}*/
bool sum[6]={false}; //布尔数组判断其真值
for(i=0;i<6;i++)
{
if(command[i]!=0)
{
sum[i]=true;
}
//printf("\n%d ",sum[i]);
}
list(sum,6);
return 0;
}
九、总结
面向项目学东西,活学活用是个很快速且有效的学习方法。
确定了方向接下来就要全力以赴了,高产博主恢复上线!!