实现my_ls
在学习linuxC编程实战我们首先会遇到如何实现ls功能,下面将简单介绍实现的方法。
ls功能介绍
-l :以长格式显示目录下的内容列表。输出的信息从左到右依次包括文件名, 文件类型、权限模式、硬连接数、所有者、组、文件大小和文件的最后修改时间等.
-a :显示所有文件和目录.
-R :递归来输出所有文件夹的文件.
-i :显示文件索引节点号(inode).
-r :逆序输出文件及目录.
-t :根据时间对文件进行排序.
-s :显示文件和目录的大小(以区块为单位).
ls的运行流程图
通过运行流程图对ls的运行过程有一个更加深刻的理解
主要函数的介绍
1.opendir
1.头文件:#include<sys/types.h>#include<dirent.h>
2.函数原型:struct dirent * opendir(DIR * dir);
3.函数功能:opendir()返回参数dir下子目录中由文件和目录组成的列表.结构体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;
4.返回值:返回参数dir下子目录中由文件和目录组成的列表,若dir为空则返回NULL.
2.readdir
1.头文件:#include <sys/types.h> #include <dirent.h>
2.函数原型:struct dirent * readdir(DIR * dir);
3.函数功能:readdir()返回参数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 d_name 所指的文件类型
har d_name[256]; //d_name 文件名
};
4.返回值:成功则返回下个目录进入点. 有错误发生或读取到目录文件尾则返回NULL.
3.lstat
1.头文件:#include <sys/types.h> #nclude <sys/stat.h> #include <unistd.h>
2.函数原型:int lstat(const char *path, struct stat *buf);(这里有三个类似的函数,stat、fstat、lstat,这里我们只了解lstat).
3.函数功能:函数返回关于文件或符号连接的信息。结构体lstat定义如下:
具体介绍:https://blog.csdn.net/qq_33883085/article/details/88695946
struct stat {
dev_t st_dev; /* 文件的设备编号 */
ino_t st_ino; /* 索引结点编号 */
mode_t st_mode; /* 文件类型和权限*/
nlink_t st_nlink; /*硬链接数 */
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; /* 块数 */
time_t st_atime; /* 访问时间 */
time_t st_mtime; /* 修改时间 */
time_t st_ctime; /* 更改时间 */
};
4.返回值:成功返回0,失败返回-1,并且将详细错误信息赋值给errno全局变量。
ls的实现:
1.头文件与宏定义
在具体实现之前我们需要了解他需要的头文件何所定义的宏.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<sys/stat.h>
#include<unistd.h>
#include<sys/types.h>
#include<linux/limits.h>
#include<dirent.h>
#include<grp.h>
#include<pwd.h>
#include<errno.h>
#include<signal.h>
//以下为宏定义,在执行过程中通过位运算来达到获取指令的效果,大大提高了运行效率
#define PARAM_A 1//-a:显示该目录下所有文件,包括隐藏文件
#define PARAM_L 2//-l:一行只显示一个文件的详细信息,包括文件的访问权限,文件大小等
#define PARAM_R 4//-R:递归读取文件
#define PARAM_T 8//-t:按照时间排序
#define PARAM_r 16//-r:按照文件逆序排序
#define PARAM_I 32//-i:显示节点信息
#define PARAM_S 64//-s:在每个文件名后输出该文件的大小
#define MAXROWLEN 80//一行显示的最多字符数
ls后无参数
当ls后没有限定参数条件时,我们直接打印(在这之前可以对文件名进行排序,以保证美观),下面是主要代码段.
char colorname[NAME_MAX + 30];
int i,len,j=0;
l++;
//如果本行不足以打印一个文件名则换行
// if(g_leave_len < g_maxlen){
// printf("\n");
// g_leave_len = MAXROWLEN;
// }
len = strlen(name);
for(i=0;i<len;i++){
if(name[i]<0){
j++;
}
}
len = g_maxlen - len + j/3;
sprintf(colorname,"\033[%dm%s\033[0m",filecolor,name);//将文件以不同颜色输出
printf("%s",colorname);
for(i=0;i<len+5;i++){
printf(" "); //每个文件名后打印五个空格
}
if(l == Line){
l=0;
printf("\n");
}
//下面的2指示空两格
g_leave_len -=(g_maxlen + 2);
ls -a
该功能是将目录下所以有目录全部显示出来,包括隐藏目录,所以只需将判断条件删去即可
if(name[0]!='.') //将自己的判断隐藏文件的条件删去
ls -l
此功能是将文件内的详细信息显示出来,所以我们需要了解文件权限的相关内容
字符常量值 | 字符常量值对应的八进制 | 含义 |
---|---|---|
S_IRUSR(S_IREAD) | 00400 | 文件所有者具有可读权限; |
S_IWUSR(S_IWRITE) | 00200 | 文件所有者具有可写入权限; |
S_IXUSR(S_IEXEC) | 00100 | 文件所有者具有可执行权限; |
S_IRGRP | 00040 | 用户组具有可读取权限; |
S_IWGRP | 00020 | 用户组具有可写入权限; |
S_IXGRP | 00010 | 用户组具有可执行权限; |
S_IROTH | 00004 | 其他用户具有可读权限; |
S_IWOTH | 00002 | 其他用户具有可写入权限; |
S_IXOTH | 00001 | 其他用户具有可执行权限; |
S_ISUID | 04000 | 文件的(set user -id on execution)位; |
S_ISGID | 02000 | 文件的(set group-id on execution)位; |
S_ISVTX | 01000 | 文件的sticky位; |
下面是打印文件属性的部分代码
char buf_time[32];
struct passwd *psd; //从该结构体中获取文件所有者的用户名
struct group *grp; //从该结构体中获取文件所有者所属的组名
//获取并打印文件类型
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");
}
//获取并打印文件所有者的权限
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(" ");
//根据uid与gid获取文件所有者的用户名和组名
psd = getpwuid(buf.st_uid);
grp = getgrgid(buf.st_gid);
printf("%4d ",buf.st_nlink); //打印文件的链接数
printf("%-8s",psd->pw_name); //打印文件所有者的用户名
printf("%-8s",grp->gr_name); //打印文件所有者的组名
printf("%6d",buf.st_size); //打印文件的大小
strcpy(buf_time,ctime(&buf.st_mtime));
buf_time[strlen(buf_time)-1] = '\0'; //去掉换行符
printf(" %s",buf_time); //打印文件的时间信息
ls -i
打印文件的节点数据
int i,j=0,len;
l++;
printf("%d ",buf.st_ino); //打印文件的节点数据
len = strlen(name);
for(i=0;i<len+5;i++){
if(name[i]<0)
j++;
}
len = g_maxlen - len + j/3;
sprintf(colorname,"\033[%dm%s\033[0m",filecolor,name);
printf("%-s", colorname);
//输出少于要求,补够空格
for(i=0;i<len;i++)
printf(" ");
if( l == Line)
{
printf("\n");
l = 0;
}
ls -s
部分代码如下:
char colorname[NAME_MAX + 30];
int i,len,j=0;
int a=0,b=0;
l++;
len=strlen(name);
for(i=0;i<len;i++)
if(name[i] < 0)
j++;
len = g_maxlen -len + j/3;
sprintf(colorname,"\033[%dm%s\033[0m",filecolor,name);
printf("%2d ",buf.st_blocks/2); //由于系统的容量大小和自己实现的不同,所以根据系统的来改变,这里是相差了2倍
printf("%-s",colorname);
//输出少于要求,补充空格
for(i=0;i<len;i++)
printf(" ");
if( l == Line)
{
printf("\n");
l=0;
}
ls -R
void display_R(int flag_param,char *path)
{
DIR *dir;
struct dirent *ptr;
int count=0;
int filecolor;
int y=0;
int flag_param_temp;
char filenames[256][PATH_MAX + 1],temp[PATH_MAX + 1];
char muluname[256][PATH_MAX + 1];
long filetime[256][1];
long t;
struct stat buf;
struct stat name;
flag_param_temp = flag_param;
//获取该目录下文件总数和最长的文件名
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++){
ptr = readdir(dir);
if(ptr == 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';
}
//使用冒泡法对文件名进行排序,排序后文件名按照字母顺序存储于filenames
if(flag_param & PARAM_T){
flag_param -= PARAM_T;
for(i=0;i<count;i++){
stat(filenames[i],&buf);
filetime[i][0] = buf.st_mtime;
}
for(i=0;i < count;i++)
for(j=i;j < count;j++){
if(filetime[i][0] < filetime[j][0]){
t = filetime[j][0];
filetime[j][0] = filetime[i][0];
filetime[i][0] = t;
strcpy(temp,filenames[j]);
strcpy(filenames[j],filenames[i]);
strcpy(filenames[i],temp);
}
}
}
else if(flag_param & PARAM_r){
flag_param -= PARAM_r;
for(i=0;i < count-1;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';
}
}
}
else{
for(i=0;i < count-1;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';
}
}
}
//计算总用量total
if(flag_param & PARAM_A)
{
for(i = 0;i<count;i++)
{
stat(filenames[i],&name);
total = total + name.st_blocks/2;
}
}
else
{
for(i = 0;i<count;i++)
{
stat(filenames[i],&name);
if(filenames[i][2] != '.')
{
total = total + name.st_blocks/2;
}
}
}
printf("\n");
printf("\n总用量:%d",total);
printf("\n%s:\n",path);
for(i = 0;i < count;i++)
{
stat(filenames[i],&buf);
if(S_ISDIR(buf.st_mode))
{
len = strlen(filenames[i]);
//-R时只有./根目录打开,其他../ ./. 等等目录不打开
if((filenames[i][len-1] =='.' && filenames[i][len-2] == '/')||(filenames[i][len-1] == '.' && filenames[i][len-2] == '.' && filenames[i][len-3] == '/'))
continue;
strncpy(muluname[y],filenames[i],len);
len = strlen(muluname[y]);
muluname[y][len] = '/';
muluname[y][len+1] = '\0';
y++;
}
display(flag_param,filenames[i]);
}
for(i = 2;i < y; i++)
{
list_dir(muluname[i],flag_param);
}
}
void list_dir(char *pathname,int param){
char nextpath[PATH_MAX+1];
int len=0;
DIR *ret_opendir = opendir(pathname);//打开目录
if(ret_opendir == NULL)
error_printf("opendir");
printf("\n%s:\n",pathname);//显示pathname的路径
display_DIR(ret_opendir,34);//显示pathname目录下所有非隐藏的文件名称
//display(param,pathname);
struct dirent*ret_readdir = NULL;//定义readdir函数返回的结构体变量
while((ret_readdir = readdir(ret_opendir))!=NULL)//判断是否读取到目录尾
{
char *filename = ret_readdir->d_name;//获取文件名
int end=0;//优化显示路径(处理./text/与./text)
while(pathname[end])
end++;
strcpy(nextpath,pathname);
if(pathname[end-1]!='/')
strcat(nextpath,"/");
strcat(nextpath,filename);
struct stat file_message = {
};//定义stat函数返回结构体变量
int ret_stat = lstat(nextpath,&file_message);//获取文件信息
len = strlen(filename);
if(ret_stat == -1)//stat读取文件错误则显示提示信息
printf("%s error!",filename);
else if(S_ISDIR(file_message.st_mode) && filename[0] != '.')//筛选‘.''..'与隐藏文件
{
list_dir(nextpath,param);
}
}
closedir(ret_opendir);
}
这里的思路是用前根序递归遍历的方法遍历,以防止遍历根目录过程中爆栈.
ls -r
通过文件名长短来进行排序.
else if(flag_param & PARAM_r){
flag_param -= PARAM_r;
for(i=0;i < count-1;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';
}
}
}
ls -t
通过解析文件时间信息来进行排序即可.
if(flag_param & PARAM_T){
flag_param -= PARAM_T;
for(i=0;i<count;i++){
stat(filenames[i],&buf);
filetime[i][0] = buf.st_mtime;
}
for(i=0;i < count;i++)
for(j=i;j < count;j++){
if(filetime[i][0] < filetime[j][0]){
t = filetime[j][0];
filetime[j][0] = filetime[i][0];
filetime[i][0] = t;
strcpy(temp,filenames[j]);
strcpy(filenames[j],filenames[i]);
strcpy(filenames[i],temp);
}
}
}
在排序的同时也要对所对应的文件进行相应排序.
文件名以不同颜色输出
首先了解linux中颜色的含义
颜色种类 | 含义 |
---|---|
白 | 普通文件 |
蓝 | 目录 |
绿 | 可执行文件 |
红 | 压缩文件 |
浅蓝色 | 链接文件 |
红色闪烁 | 链接文件出现问题 |
黄色 | 设备文件 |
灰色 | 其他文件 |
使用sprintf函数来进行颜色的改变
sprintf(colorname,"\033[%dm%s\033[0m",filecolor,ret_readdir->d_name);
F(前景色) | B(背景色) | 颜色 |
---|---|---|
30 | 40 | 黑 |
31 | 41 | 红 |
32 | 42 | 绿 |
33 | 43 | 黄 |
34 | 44 | 蓝 |
35 | 45 | 紫红 |
36 | 46 | 青蓝 |
37 | 47 | 白 |
屏蔽ctrl + c杀死程序
在这里我们使用signal函数来实现.
1.signal(参数1,参数2);
参数1::我们要进行处理的信号(系统定义的宏)。系统的信号我们可以再终端键入 kill -l查看(共64个).
参数2:我们所要处理的方式(是系统默认还是忽略还是捕捉).
2.头文件:#include<signal.h>
3.函数功能:
4.具体实现:
signal(SIGINT,Signhandler);
int main(int argc,char*argv[]){
signal(SIGINT,Signhandler);
printf("获取信号 %d,跳出...\n",signum);
exit(1);
}
举个例子
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
void signalhander(int signum){
printf("get signal %d",signum);
}
int main(int argc,char* argv[]){
//signal(SIGINT,signalhander);
int i=0;
int a[10];
for(i=0;i<10;i++)
scanf("%d",&a[i]);
return 0;
}
当我们在运行这个程序时使用ctrl + c可以直接杀死程序
但是当使用signal函数时我们就无法跳出程序
以上就是对于我对my_ls小结,希望大佬指点,谢谢。