my_ls小结

实现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_IRGRP00040用户组具有可读取权限;
S_IWGRP00020用户组具有可写入权限;
S_IXGRP00010用户组具有可执行权限;
S_IROTH00004其他用户具有可读权限;
S_IWOTH00002其他用户具有可写入权限;
S_IXOTH00001其他用户具有可执行权限;
S_ISUID04000文件的(set user -id on execution)位;
S_ISGID02000文件的(set group-id on execution)位;
S_ISVTX01000文件的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(背景色)颜色
3040
3141
3242绿
3343
3444
3545紫红
3646青蓝
3747

屏蔽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小结,希望大佬指点,谢谢。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值