Linux中ls -a,-l,-r,-R命令的实现

ls简介

ls是我们使用Linux最常用的命令,可以用来打印当前目录或者制定目录的清单,显示出文件的一些信息等。
ls -a可以将目录下的全部文件(包括隐藏文件)显示出来
ls -l 列出长数据串,包括文件的属性和权限等数据
ls -r将排序结果反向输出,例如:原本文件名由小到大,反向则由大到小
ls -R连同子目录一同显示出来,也就所说该目录下所有文件都会显示出来(显示隐藏文件要加-a参数)
简单的介绍就到这了,我们放码说话。

代码

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

#define PARAM_NONE 0        //由于后面要用到位运算,所以介绍一下,当没有参数的时候flag=0;然后依次是-a,-l,-R,-r,定义为1,2,4,8
#define PARAM_A    1        //刚好就是二进制中的1,10,100,1000。这样方便为运算中的|和&,比如同时有a和r参数,那么flag就为1001.
#define PARAM_L    2        //当要判断是否含有这两个参数中的一个时(比如r参数)就可以用flag&PARAM_r,如果为0的话就没有r这个参数。
#define PARAM_R    4        //其他的也类似
#define PARAM_r    8
#define MAXROWLEN  80

char PATH[PATH_MAX+1];      //用于存储路径
int flag;

int g_leave_len = MAXROWLEN;
int g_maxlen;

void my_err(const char* err_string,int line);
void display_dir(char* path);

void my_err(const char* err_string,int line)
{
    fprintf(stderr,"line:%d",__LINE__);
    perror(err_string);
    exit(1);
}

void cprint(char* name,mode_t st_mode)
{

    if(S_ISLNK(st_mode))   //链接文件
        printf("\033[1;36m%-*s\033[0m",g_maxlen,name);
    else if(S_ISDIR(st_mode)&&(st_mode&000777)==0777)   //满权限的目录
        printf("\033[1;34;42m%-*s  \033[0m",g_maxlen,name);
    else if(S_ISDIR(st_mode))  //目录
                printf("\033[1;34m%-*s  \033[0m",g_maxlen,name);
    else if(st_mode&S_IXUSR||st_mode&S_IXGRP||st_mode&S_IXOTH) //可执行文件
        printf("\033[1;32m%-*s  \033[0m",g_maxlen,name);
    else   //其他文件
        printf("%*s  ",g_maxlen,name);
}
void  display_attribute(char* name)  //-l参数时按照相应格式打印
{
    struct stat buf;
    char buff_time[32];
    struct passwd* psd;  //从该结构体接收文件所有者用户名
    struct group* grp;   //获取组名
    if(lstat(name,&buf)==-1)
    {
        my_err("stat",__LINE__);
    }
    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(buff_time,ctime(&buf.st_mtime));
    buff_time[strlen(buff_time)-1]='\0'; //buff_time自带换行,因此要去掉后面的换行符
    printf("  %s  ",buff_time);
    cprint(name,buf.st_mode);
    printf("\n");
}
void  displayR_attribute(char* name)  //当l和R都有时,先调用display_attribute打印,然后该函数负责递归
{
    struct stat buf;

    if(lstat(name,&buf)==-1)
    {
        my_err("stat",__LINE__);
    }
    if(S_ISDIR(buf.st_mode))
    {
            display_dir(name);
            free(name);
            char* p=PATH;
            while(*++p);
            while(*--p!='/');
            *p='\0';             //每次递归完成之后将原来的路径回退至递归前
            chdir("..");         //跳转到当前目录的上一层目录
            return;
    }
}
void display_single(char* name)   //打印文件
{
    int len ;
    struct stat buf;
    if(lstat(name,&buf)==-1)
    {
        return;
    }

    if(g_leave_len<g_maxlen)
    {
        printf("\n");
        g_leave_len=MAXROWLEN;
    }

    cprint(name,buf.st_mode);  //根据文件的不同类型显示不同颜色
    g_leave_len=g_leave_len-(g_maxlen+2);

}

void displayR_single(char* name)  //当有-R参数时打印文件名并调用display_dir
{
    int len ;
    struct stat buf;
    if(lstat(name,&buf)==-1)
    {
        return;
    }
    if(S_ISDIR(buf.st_mode))    
    {
            printf("\n");

            g_leave_len=MAXROWLEN;

            display_dir(name);
            free(name);   //将之前filenames[i]中的空间释放
            char*p=PATH;

            while(*++p);
            while(*--p!='/');
            *p='\0';
            chdir("..");  //返回上层目录
    }

}

void display(char **name ,int count)  //根据flag去调用不同的函数
{
    switch(flag)
    {
        int i;
        case PARAM_r:
        case PARAM_NONE:
            for(i=0;i<count;i++)
            {
                if(name[i][0]!='.') //排除.., .,以及隐藏文件
                    display_single(name[i]);
            }
            break;
        case PARAM_r+PARAM_A:
        case PARAM_A:
            for(i=0;i<count;i++)
            {
                display_single(name[i]);
            }
            break;
        case PARAM_r+PARAM_L:
        case PARAM_L:
        for(i=0;i<count;i++)
        {
            if(name[i][0]!='.')
            {
                display_attribute(name[i]);

            }
        }
            break;
        case PARAM_R+PARAM_r:
        case PARAM_R:
            for(i=0;i<count;i++)
            {
                if(name[i][0]!='.')
                {
                    display_single(name[i]);
                }
            }
            for(i=0;i<count;i++)
            {
                if(name[i][0]!='.')   //排除 "."和".."两个目录,防止死循环,下同
                {
                    displayR_single(name[i]);
                }
            }
            break;
        case PARAM_L+PARAM_r+PARAM_R:
        case PARAM_R+PARAM_L:
            for(i=0;i<count;i++)
            {
                if(name[i][0]!='.')
                {
                    display_attribute(name[i]);
                }
            }
            for(i=0;i<count;i++)
            {
                if(name[i][0]!='.')
                {
                    displayR_attribute(name[i]);
                }
            }
            break;
        case PARAM_A+PARAM_r+PARAM_R:
        case PARAM_R+PARAM_A:
            for(i=0;i<count;i++)
            {
                    display_single(name[i]);
            }
            for(i=0;i<count;i++)
            {
                if(name[i][0]!='.')
                {
                    displayR_single(name[i]);
                }
            }
            break;

        case PARAM_A+PARAM_L+PARAM_r:
        case PARAM_A+PARAM_L:
            for(i=0;i<count;i++)
            {
                display_attribute(name[i]);
            }
            break;
        case PARAM_A+PARAM_L+PARAM_R+PARAM_r:
        case PARAM_A+PARAM_L+PARAM_R:
            for(i=0;i<count;i++)
            {
                display_attribute(name[i]);
            }
            for(i=0;i<count;i++)
            {
                if(name[i][0]!='.')
                {
                    displayR_attribute(name[i]);
                }
            }
            break;
        default:
            break;
    }
}


void display_dir(char* path)      //该函数用以对目录进行处理
{
    DIR* dir;                     //接受opendir返回的文件描述符
    struct dirent* ptr;           //接受readdir返回的结构体
    int count=0;
    //char filenames[300][PATH_MAX+1],temp[PATH_MAX+1];  这里是被优化掉的代码,由于函数中定义的变量
    //是在栈上分配空间,因此当多次调用的时候会十分消耗栈上的空间,最终导致栈溢出,在linux上的表现就是核心已转储
    //并且有的目录中的文件数远远大于300

    if((flag&PARAM_R)!=0)               //作为一个强迫症,绝不允许格式上出问题
    {
        int len =strlen(PATH);
        if(len>0)
        {
            if(PATH[len-1]=='/')
                PATH[len-1]='\0';
        }
        if(path[0]=='.'||path[0]=='/')
        {
            strcat(PATH,path);
        }
        else
        {
            strcat(PATH,"/");
            strcat(PATH,path);
        }
        printf("%s:\n",PATH);
    }
    //获取文件数和最长文件名长度 
    dir = opendir(path);
    if(dir==NULL)
        my_err("opendir",__LINE__);
    g_maxlen=0;
    while((ptr=readdir(dir))!=NULL)
    {
        if(g_maxlen<strlen(ptr->d_name))
            g_maxlen=strlen(ptr->d_name);
        count++;
    }
    closedir(dir);
    char **filenames=(char**)malloc(sizeof(char*)*count),temp[PATH_MAX+1];  //通过目录中文件数来动态的在堆上分配存储空间,首先定义了一个指针数组
    for(int i=0;i<count;i++)                                                //然后依次让数组中每个指针指向分配好的空间,这里是对上面的优化,有效
    {                                                                       //的防止了栈溢出,同时动态分配内存,更加节省空间
        filenames[i]=(char*)malloc(sizeof(char)*PATH_MAX+1);
    }

    int i,j;
    //获取该目录下所有的文件名
    dir=opendir(path);
    for(i=0;i<count;i++)
    {
        ptr=readdir(dir);
        if(ptr==NULL)
        {
            my_err("readdir",__LINE__);
        }
        strcpy(filenames[i],ptr->d_name);
    }
    closedir(dir);
    //冒泡法对文件名排序
    if(flag&PARAM_r)  //-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]);
                    strcpy(filenames[j],filenames[j+1]);
                    strcpy(filenames[j+1],temp);
                }
            }
        }
    }
    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]);
                                        strcpy(filenames[j],filenames[j+1]);
                                        strcpy(filenames[j+1],temp);
                                }
                        }
                }

    }

    if(chdir(path)<0)
    {
        my_err("chdir",__LINE__);
    }

    display(filenames,count);
    if((flag&PARAM_L==0&&!(flag|PARAM_R)))
        printf("\n");
}

int main(int argc,char** argv)
{
    int i,j,k=0,num=0;
    char param[32]="";     //用来保存命令行参数
    char *path[1];         //保存路径,其实我也不想定义为一个指针数组,但是为了和后面的char **类型的函数参数对应只能定义成这样
    path[0]=(char*)malloc(sizeof(char)*(PATH_MAX+1));   //由于是一个指针类型,所以我们要给他分配空间  PATH_MAX是系统定义好的宏,它的值为4096,严谨一点,加1用来存'\0'
    flag=PARAM_NONE;       //初始化flag=0(由于flag是全剧变量,所以可以不用初始化)
    struct stat buf;       //该结构体以及lstat,stat,在 sys/types.h,sys/stat.h,unistd.h 中,具体 man 2 stat

    //命令行参数解析,将-后面的参数保存至param中
    for(i=1;i<argc;i++)
    {
        if(argv[i][0]=='-')
        {
            for(j=1;j<strlen(argv[i]);j++)
            {
                param[k]=argv[i][j];
                k++;    
            }
            num++;
        }
    }
    param[k]='\0';

    /* 判断参数 */
    for(i=0;i<k;i++)
    {
        if(param[i]=='a')
            flag|=PARAM_A;
        else if(param[i]=='l')
            flag|=PARAM_L;
        else if(param[i]=='R')
            flag|=PARAM_R;
        else if(param[i]=='r')
            flag|=PARAM_r;
        else
        {
            printf("my_ls:invalid option -%c\n",param[i]);
            exit(0);
        }
    }

    //如果没有输入目标文件或目录,就显示当前目录
    if(num+1==argc)
    {
        strcpy(path[0],".");
        display_dir(path[0]);
        return 0;
    }

    i=1;
    do
    {
        if(argv[i][0]=='-')
        {
            i++;
            continue;
        }
        else
        {
            strcpy(path[0],argv[i]);
            //判断目录或文件是否存在
            if(stat(argv[i],&buf)==-1)
            {
                my_err("stat",__LINE__);    //编译器内置的宏,插入当前源码行号
            }
            if(S_ISDIR(buf.st_mode))  //判断是否为目录
            {
                display_dir(path[0]);
                i++;
            }
            else
            {
                display(path,1);
                i++;
            }
        }
    }while(i<argc);
    printf("\n");

    return 0;

}

注意

在我们用ls -R / 这条命令时,我们首先应当切换到root用户,因为一些目录只有root用户才能打开,而在我们的程序中如果打开目录失败则会报错,并终止程序。
以及在访问“/proc”的时候会出现找不到文件的情况,我们应当忽略这种错误,让程序继续进行下去,因为/proc是虚拟文件系统,在系统运行过程中proc中的文件在不断的变化,因此找不到文件属于正常现象,如果我们使用perror报错的话,程序在报错后会自动结束,因此我们应当将perror更改为其他方式的报错,或者,直接去掉这方面的报错。
由于我使用的是fedora的系统在用自己的程序在root权限下递归访问根目录的时候没有出现权限不足的情况,因此就没有更改报错的函数。
如果大家遇到连root权限都不足的情况(因为有的目录是root用户都无法打开的),比如我的一些小伙伴在用系统自带的ls ,执行”sudo ls -alR /run/user/1000”时会提示权限不足,,此时我们可以在lstat的错误处理函数中再加一个判断选项,判断errno是否等于13,如果等于13代表没有访问该目录的权限。此时我们可以或略这个目录,直接return,去访问其他目录即可。(这里要谢谢提供帮助的学长!)

  • 8
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Linux系统ls -l命令是用以列表的形式来查看当前目录下的文件或目录。所列出的信息包括文件或目录的权限、所有者、所属用户组、文件大小、创建时间等等。 在ls -l命令的输出,文件或目录的大小是以字节为单位表示的。而文件夹的大小始终为4K字节(4096字节),不管文件夹包含多少文件或大小不同的文件。这是因为在Linux系统,文件夹也是一个文件,并且固定占用4K字节的磁盘空间。文件夹存储了大量关于该文件夹的信息。 在ls -l命令输出,还可以看到文件的权限信息。文件的权限由9个字符组成,分别表示文件所有者、所属用户组和其他用户对文件的读、写、执行权限。其,r表示可读权限,w表示可写权限,x表示可执行权限。另外,有时候还会出现s和t的权限字符,s表示网络接口程序,t表示临时文件。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Linuxls -l命令展示信息详解](https://blog.csdn.net/qq_55754838/article/details/128379196)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Linux ls -l 命令详解](https://blog.csdn.net/Black_Cat_33/article/details/120561163)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值