Project2:用C语言实现自己的ls命令/my_ls

8 篇文章 0 订阅
8 篇文章 3 订阅

目录

一、任务说明

二、命令解析

三、命令终端效果展示

四、大致实现思路

五、棘手点

六、一些小点

七、参考书籍

八、代码展示part

九、总结


一、任务说明

lsGNU/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--->在每个文件名后输出该文件的大小


 四、大致实现思路

  1. 先通过终端调用命令后,观察其输出内容的格式;
  2. 然后逐个分析实现参数,分块完成;
  3. 每个参数在整体实现中用自定义函数的形式独立完成,结合作用函数。

五、棘手点

  • 整套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;
}

九、总结

面向项目学东西,活学活用是个很快速且有效的学习方法。

确定了方向接下来就要全力以赴了,高产博主恢复上线!!

  • 10
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

颜 然

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值