my_ls 实现

本文深入探讨了Linux系统中`ls`命令的实现原理,讲解了核心函数如opendir、readdir、stat/lstat的功能,并介绍了如何通过这些函数实现`-a`、`-l`和`-R`参数。文章通过代码示例展示了如何遍历目录、处理文件属性以及递归显示子目录内容,同时讨论了错误处理和内存管理。最后,作者指出代码仍有优化空间,表示将继续改进。
摘要由CSDN通过智能技术生成

MY_LS(-a -l -R)

一、主要函数

1.opendir

头文件:
#include<sys/types.h>
#include<dirent.h>
函数原型:
DIR* opendir (const char * path );
(获取path子目录下的所由文件和目录的列表,如果path是个文件则返回值为NULL)
功能:
打开一个目录,在失败的时候返回一个空的指针。
返回值(DIR):
DIR 结构体的原型为:struct_dirstream

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;
2.readdir

头文件:
#include<dirent.h>
函数原型:
struct dirent* readdir(DIR* dir_handle);
功能:
读取opendir 返回值的那个列表
返回值:
返回dirent结构体指针,dirent结构体成员如下,(文件和目录都行)

struct dirent   
{   
  long d_ino; /* inode number 索引节点号 */  
     
    off_t d_off; /* offset to this dirent 在目录文件中的偏移 */  
     
    unsigned short d_reclen; /* length of this d_name 文件名长 */  
     
    unsigned char d_type; /* the type of d_name 文件类型 */  
     
    char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */  
}
3.stat / lstat

头文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
函数原型:
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
第一个形参:指出文件(文件路径); 第二个形参:出参数(函数对该参数操作,然后传出)。
区别:
lstat函数的形参跟stat函数的形参一样。其功能也跟stat函数功能一样,仅有一点不同:stat函数是穿透(追踪)函数,即对软链接文件进行操作时,操作的是链接到的那一个文件,不是软链接文件本身;而lstat函数是不穿透(不追踪)函数,对软链接文件进行操作时,操作的是软链接文件本身。
返回值:
成功返回0,失败返回-1,并且将详细错误信息赋值给errno全局变量。

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.getcwd

头文件:
#include <unistd.h>
函数原型:
char * getcwd(char * buf, size_t size);
功能:
getcwd()会将当前的工作目录绝对路径复制到参数buf 所指的内存空间,参数size 为buf 的空间大小。
返回值:
执行成功则将结果复制到参数buf 所指的内存空间, 或是返回自动配置的字符串指针. 失败返回NULL,错误代码存于errno.

注:
1、在调用此函数时,buf 所指的内存空间要足够大。若工作目录绝对路径的字符串长度超过参数size 大小,则返回NULL,errno 的值则为ERANGE。
2、倘若参数buf 为NULL,getcwd()会依参数size 的大小自动配置内存(使用malloc()),如果参数size 也为0,则getcwd()会依工作目录绝对路径的字符串程度来决定所配置的内存大小,进程可以在使用完次字符串后利用free()来释放此空间。

5.chdir

头文件:
#include <unistd.h>
函数原型:
int chdir(const char * path);
功能:
chdir()用来将当前的工作目录改变成以参数path 所指的目录.
返回值:
执行成功则返回0, 失败返回-1, errno 为错误代码.

二、主体思路

通过《LinuxC编程实战》的学习,先对ls命令有初步整体的认识,在研究每一个参数各自的实现过程。
1.先解析命令行参数,判断是否有-a -l -R以及是否有目录或文件。
2.-a可以通过文件名首是否为".“判断,不用单独函数实现。
3.-l用lstat函数解析,打印即可。
4.-R的实现需要递归调用,我遇到的问题是一定要在切换到当前目录再进行递归,否则找不到目录或文件。一定要排除” . " " … "的可能性。一定要在适当的位置返回上一层目录。

三、实现代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>
#include<dirent.h>
#include<pwd.h>
#include<grp.h>
#include<time.h>
#include <signal.h>

#define PARAM_NONE 0
#define PARAM_A 1
#define PARAM_L 2
#define PARAM_R 4
#define MAXLEN  80              //一行显示的最多字符数
#define PATH_MAX 2000
#define NAME_MAX 256

int g_leave_len = MAXLEN;       //一行剩余长度,用于输出对齐
int g_maxlen;                   //存放某目录下最长文件名的长度


int flag = 0;
char *filename[50000];

//屏蔽ctrl+c
static void mask_ctrl_c()          //???
{
    sigset_t intmask;
    sigemptyset(&intmask);//将信号集合设置为空
    sigaddset(&intmask,SIGINT);//加入中断 Ctrl+C 信号
    //阻塞信号
    sigprocmask(SIG_BLOCK,&intmask,NULL);
}

//错误处理
void error(const char *err_string , int line)
{
    fprintf(stderr,"line:%d",line);
    perror(err_string);
    exit(1);
}

//???
char * get_exe_path( char *path ,char * buf, int count)
{
    int i;
    int rslt = readlink(path, buf, count - 1);
    if (rslt < 0 || (rslt >= count - 1))
    {
        return NULL;
    }
    buf[rslt] = '\0';
    for (i = rslt; i >= 0; i--)
    {
        if (buf[i] == '/')
        {
            buf[i + 1] = '\0';
            break;
        }
    }
    return buf;
}

void display(char name[][NAME_MAX],int num)
{
    int i,j=0;
    for(i=0;i<num;i++)
    {
        j++;
        printf("%-20s",name[i]);
        if(j==5)
        {
            printf("\n");
            j = 0;
        }
    }
    printf("\n");
}

int display_attribute(struct stat buf ,char *name)
{
    char time[64];
    char hen[1024];
    struct passwd *pwd; //从该结构体获取文件所有者的名字
    struct group *grp; //从该结构体获取文件组名

    //判断文件类型
    if(S_ISLNK(buf.st_mode))	//判断是否为链接
        printf("l");
    if(S_ISREG(buf.st_mode))	//判断是否为一般文件
        printf("-");
    if(S_ISDIR(buf.st_mode))	//判断是否为目录
        printf("d");
    if(S_ISCHR(buf.st_mode))	//字符设备文件
        printf("c");
    if(S_ISBLK(buf.st_mode))	//块设备文件
        printf("b");
    if(S_ISFIFO(buf.st_mode))	//先进先出FIFO
        printf("f");
    if(S_ISSOCK(buf.st_mode))	//socket
        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(" %4d ",buf.st_nlink);

    //打印文件用户和用户组
    pwd = getpwuid(buf.st_uid);
    grp = getgrgid(buf.st_gid);
    printf("%-10s",pwd->pw_name);
    printf("%-8s",grp->gr_name);

    //打印文件大小
    printf("%6lld",buf.st_size);

    //打印文件最后一次修改的时间
    strcpy(time,ctime(&buf.st_ctime));
    time[strlen(time)-1] = '\0';
    printf(" %s",time);

    //打印文件名
    if(S_ISLNK(buf.st_mode))
        printf("%s\n",get_exe_path(name,(char*)hen,1024));    //???
    else
        printf(" %s\n",name);
    return 0;
}


//ls -a  ls
void display_dir(char * path)
{
    DIR *dir;
    int i = 0;
    int number = 0;
    char a[10000][256];
    struct dirent *ptr;
    dir = opendir(path);
    if(dir == NULL)
        error("opendir",__LINE__);
    while((ptr = readdir(dir))!=NULL)
    {
        if(ptr->d_name[1] == '.' && flag == 0){
            continue;
        }
        strcpy(a[i],ptr->d_name);
        a[i][strlen(a[i])+1] = '\0';
        i++;
        number++;
    }
    display(a,number);
}


//ls -l -al
void display2(char* pathname) {
    int i, j;
    struct stat buf;
    char name[NAME_MAX + 1];

    //从路径解析文件名
    for (i = 0, j = 0; i < strlen(pathname); i++) {
        if (pathname[i] == '/') {
            j = 0;
            continue;
        }
        name[j++] = pathname[i];    //???
    }
    name[j] = '\0';

    //用lstat解析链接文件
    if (lstat(pathname, &buf) == -1) {
        error("stat", __LINE__);
    }
    if (name[0] != '.' ) {
        display_attribute(buf, name);
    }
}

//ls -l  -al
void display_dir2(char *path)
{
//DIR *dir;
    int i = 0;
    int number = 0;
    DIR *dir2;
    struct stat buf;
    char a[10000][256];
    char temp[PATH_MAX + 1];
    struct dirent *ptr;
    //dir = opendir(path);
    DIR* dir = opendir(path);
    if(dir == NULL)
    {
        perror("open dir error");
        return ;
    }

    if(dir == NULL){
        error("opendir",__LINE__);
    }
    while((ptr = readdir(dir)) != NULL){
        /*if(ptr->d_name[1] == '.' && flag == 0)
            continue;
        if(lstat(ptr->d_name,&buf)<0)
            continue;*/
        //strcpy(a[i],ptr->d_name);
        //a[i][strlen(ptr->d_name)+1] = '\0';
        //i++;
        number++;
    }
    closedir(dir);
    int len = strlen(path);
    dir = opendir(path);
    for(i = 0;i < number;i++){
        ptr = readdir(dir);
        if(ptr == NULL){
            error("opendir",__LINE__);
        }
        strcpy(a[i],path);
        a[i][len] = '\0';
        strcat(a[i],ptr->d_name);
        a[i][len+strlen(ptr->d_name)] = '\0';
    }
    /*for(i=0;i<number;i++)
    {
        printf("%s",a[i]);
        printf("\n");
        if(lstat(a[i],&buf)==-1)
            continue;
        //printf("%s",a[i]);
        //printf("\n");
        if(S_ISDIR(buf.st_mode))
        {
            if((dir2 = opendir(a[i])) < 0)
                continue;
        }
        display_attribute(buf,a[i]);
    }*/
    int j;
    for(i = 0;i < number-1;j++) {
        for (j = 0; j < number - 1 - i; j++) {
            if (strcmp(a[j], a[j + 1]) > 0) {
                strcpy(temp, a[j + 1]);
                temp[strlen(a[j + 1])] = '\0';
                strcpy(a[j + 1], a[j]);
                a[j + 1][strlen(a[j])] = '\0';
                strcpy(a[j], temp);
                a[j][strlen(temp)] = '\0';
            }
        }
        for (i = 0; i < number; i++) {
            display2(a[i]);
        }
        closedir(dir);
    }
}

void display_R(char *path)
{
    DIR * dir;
    struct dirent  *ptr;
    int i,count = 0;
    struct stat buf;
    char name[80];
    int sign = 0;
    printf("\n");
    getcwd(name,sizeof(name));
    printf("\n%s:\n",name);
    chdir(path);
    dir = opendir(path);     
    if(dir==NULL){
        error("opendir",__LINE__);
    }
    while((ptr = readdir(dir))!=NULL){
        count++;
    }
    char ** filenames=(char**)malloc(sizeof(char*)*20000);
    for(int i=0;i<count;i++){
        filenames[i]=(char*)malloc(sizeof(char)*PATH_MAX+1);
    }
    closedir(dir);

    dir = opendir(path);
    for(i=0;i<count;i++)
    {
        ptr = readdir(dir);
        if(ptr == NULL){
            printf("readdir error! line:%d",__LINE__);
        }
        if(strcmp(ptr->d_name,"..") != 0 && strcmp(ptr->d_name,".") != 0){
            printf("%-15s ",ptr->d_name);
            sign+=1;
        }
        if(sign == 4){
			printf("\n");
			sign = 0;
		}
        strcat(filenames[i],ptr->d_name);    
    }
    for(i=0;i<count;i++){
          /*if(lstat(filenames[i],&buf)==-1){
              error("lstat",__LINE__);
              printf("%s",filenames[i]);
              return ;
          }*/
          if(strcmp(filenames[i],"..")==0)
          continue;
          if(strcmp(filenames[i],".")==0)
          continue;
          lstat(filenames[i],&buf);
          if(S_ISDIR(buf.st_mode))
          {
            chdir(filenames[i]);
            display_R("./");
            chdir("../");
          }
          else if(!S_ISDIR(buf.st_mode))
          {
             continue;
          }       
    }

    for(i=0;i<count;i++){
        free(filenames[i]);
    }
    free(filenames);
    closedir(dir);        
}

void display_LR(char *path)
{
    DIR * dir;
    struct dirent  *ptr;
    int i,count = 0;
    struct stat buf;
    int sign = 0;
    char name[80];
    printf("\n");
    getcwd(name,sizeof(name));
    printf("\n%s:\n",name);
    chdir(path);
    dir = opendir(path);
    if(dir==NULL){
        error("opendir",__LINE__);
    }
    while((ptr = readdir(dir))!=NULL){
        count++;
    }
    char ** filenames=(char**)malloc(sizeof(char*)*20000);
    for(int i=0;i<count;i++){
        filenames[i]=(char*)malloc(sizeof(char)*PATH_MAX+1);
    }
    closedir(dir);

    int len = strlen(path);
    dir = opendir(path);
    for(i=0;i<count;i++)
    {
        ptr = readdir(dir);
        if(ptr == NULL){
            error("opendir",__LINE__);
        }
        strcpy(filenames[i],path);
        filenames[i][len] = '\0';
        strcat(filenames[i],ptr->d_name);
        filenames[i][len+strlen(ptr->d_name)] = '\0';
        //lstat(filenames[i],&buf);
        //display_attribute(buf ,filenames[i]);
    }
    for (i = 0; i < count; i++) {
            display2(filenames[i]);
        }
    closedir(dir);
    for(i=0;i<count;i++){
        if(lstat(filenames[i],&buf)==-1){
            error("lstat",__LINE__);
            printf("%s",filenames[i]);
            return ;
        }
        ptr = readdir(dir);
        if(strcmp(ptr->d_name,".")==0)
			continue;
		if(strcmp(ptr->d_name,"..") == 0)
			continue;
		if(strcmp(ptr->d_name,"proc")==0)
			continue;
		if(ptr->d_name[0] == '.')
			continue;
		if(strcmp(ptr->d_name,"OS")==0)
			continue;
        lstat(filenames[i],&buf);
        if(S_ISDIR(buf.st_mode))
        {
            chdir(filenames[i]);
            display_LR("./");
            chdir("../");
        }
        else if(!S_ISDIR(buf.st_mode))
        {
            continue;
        }
        //chdir("../");
    }

    for(i=0;i<count;i++){
        free(filenames[i]);
    }
    free(filenames);
    closedir(dir);
}

int main(int argc, char *argv[])
{
    char path[PATH_MAX+1];
    //char param[32];//保存命令行参数,目标文件和目录名不在此列
    int flag_param = PARAM_NONE;//参数种类
    struct stat buf;
    int i,j,k,num=0;

    mask_ctrl_c();     //ctrl+c屏蔽

    //命令行参数
    if(argc==1)
    {
        strcpy(path,"./");
        display_dir(path);
        return 0;
    }
    for(i=1;i<argc;i++){
        if(argv[i][0] == '-'){
            k = strlen(argv[i]);
            for(j=0;j<k;j++)
            {
                if(argv[i][j] == 'a'){
                    flag_param |= PARAM_A;
                    continue;
                }
                if(argv[i][j] == 'l'){
                    flag_param |= PARAM_L;
                    continue;
                }
                if(argv[i][j] == 'R'){
                    flag_param |= PARAM_R;
                    continue;
                }
            }
        }
        else{
            strcpy(path,argv[i]);
            num++;
        }
    }
    i--;
    if(num !=0){
        if(stat(path,&buf) == -1){
            error("path",__LINE__);
        }
        if(S_ISDIR(buf.st_mode))
        {
            if(path[strlen(argv[i])-1] != '/')
            {
                path[strlen(argv[i])] = '/';
                path[strlen(argv[i])+1] = '\0';
            }
            switch(flag_param)
            {
                case 1:
                {
                    flag = 1;
                    display_dir(path);
                    break;
                }
                case 2:
                {
                    flag = 0;
                    display_dir2(path);
                    break;
                }
                case 3:
                {
                    flag = 1;
                    display_dir2(path);
                    break;
                }
                case 4:
                {
                    display_R(path);
                    break;
                }
                case 5:
                {
                    display_R(path);
                    break;
                }
                case 6:
                {
                    display_LR(path);
                    break;
                }
                case 7:
                {
                    display_LR(path);
                    break;
                }
                default:break;
            }
        }
        else
        {
            lstat(path,&buf);
            switch(flag_param)
            {
                case 1:
                {
                    printf("%s\n",path);
                    break;
                }
                case 2:
                {
                    display_attribute(buf,path);
                    break;
                }
                case 3:
                {
                    display_attribute(buf,path);
                    break;
                }
                case 4:
                {
                    printf("%s\n",path);
                    break;
                }
                case 5:
                {
                    printf("%s\n",path);
                    break;
                }
                case 6:
                {
                    display_attribute(buf,path);
                    break;
                }
                case 7:
                {
                    display_attribute(buf,path);
                    break;
                }
                default:break;
            }
        }
    }
    else{
        strcpy(path,"./");
        switch(flag_param)
        {
            case 1:
            {
                flag = 1;
                display_dir(path);
                break;
            }
            case 2:
            {
                flag = 0;
                display_dir2(path);
                break;
            }
            case 3:
            {
                flag = 1;
                display_dir2(path);
                break;
            }
            case 4:
            {
                display_R(path);
                break;
            }
            case 5:
            {
                flag = 1;
                display_R(path);
                break;
            }
            case 6:
            {
                display_LR(path);
                break;
            }
            case 7:
            {
                flag = 1;
                display_R(path);
                break;
            }
            default:break;
        }
    }
    return 0;
}
四、总结

从看书的毫无头绪到慢慢理解,是一个不断总结的过程。
目前代码不够简洁和规范,在malloc和free等处还有一些问题,会继续进行优化和调整。
未完待续,会更好!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值