C语言实现Linux的ls命令

2020/01/04 Linux apue 8250 words views

2020年第一篇正儿八经的博客,好好学习下Linux,用C语言实现Linux下的ls命令,很基础的知识……

0x01 基础知识

先看下DIR结构体、dirent结构体和stat结构体

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;

struct dirent {
    ino_t          d_ino;       /* inode number 索引节点号*/
    off_t          d_off;       /* offset to the next dirent 在目录文件中的偏移 */
    unsigned short d_reclen;    /* length of this record 文件名长*/
    unsigned char  d_type;      /* type of file; not supported by all file system types 文件类型 */
    char           d_name[256]; /* filename 文件名*/
};

struct stat {
        mode_t     st_mode;       //文件访问权限
        ino_t      st_ino;       //索引节点号
        dev_t      st_dev;        //文件使用的设备号
        dev_t      st_rdev;       //设备文件的设备号
        nlink_t    st_nlink;      //文件的硬连接数
        uid_t      st_uid;        //所有者用户识别号
        gid_t      st_gid;        //组识别号
        off_t      st_size;       //以字节为单位的文件容量
        time_t     st_atime;      //最后一次访问该文件的时间
        time_t     st_mtime;      //最后一次修改该文件的时间
        time_t     st_ctime;      //最后一次改变该文件状态的时间
        blksize_t st_blksize;    //包含该文件的磁盘块的大小
        blkcnt_t   st_blocks;     //该文件所占的磁盘块
};

目录操作相关函数

//功能:打开一个目录; 返回值:成功则返回DIR*型态的目录流, 打开失败则返回NULL.
DIR* opendir(constchar * path);

//功能:循环读取dp中的文件和目录,每读取一个文件或目录都返回一个dirent结构体指针; 返回值:读取失败返回NULL
struct dirent* readdir(DIR* dp);

//功能:关闭参数dp所指的目录流; 返回值:关闭成功则返回0,,失败返回-1
int closedir(DIR*dp);

//功能:通过文件名file_name获取文件信息,并保存在buf所指的结构体stat中; 返回值:执行成功则返回0,失败返回-1
int stat(const char *file_name,struct stat *buf);

0x02 ls的C语言实现

先实现个最简单的V1.0版本

#include<stdio.h>
#include<sys/types.h>
#include<dirent.h>

int main(int argc, char *argv[]){

    DIR *dp;
    struct dirent *dirp;

    if(argc != 2){
        printf("usage: ls directory_name\n");
    }

   if((dp = opendir(argv[1])) == NULL){
        printf("can't open %s\n", argv[1]);
   }
   while((dirp = readdir(dp)) != NULL){
        printf("%s\n", dirp->d_name);
   }
   closedir(dp);
   exit(0);
}

封装一下

#include<stdio.h>
#include<sys/types.h>
#include<dirent.h>

void do_ls(char[]);

int main(int argc, char *argv[])
{
    if(argc==1)
        do_ls(".");
    do_ls(argv[1]);
        
    return 0;
}

void do_ls(char dirname[])
{
    DIR *dp;
    struct dirent *dirp;

    if((dp=opendir(dirname))==NULL)
        fprintf(stderr,"can't open %s\n",dirname);
    else
    {
        while((dirp=readdir(dp))!=NULL)
            printf("%s\n",dirp->d_name);
        closedir(dp);
    }
}

再实现个V2.0版本使用stat结构体可以列出每个文件详细信息

#include<stdio.h>
#include<sys/types.h>
#include<dirent.h>
#include<sys/stat.h>

//代码的报错处理没写好

//列出文件的信息
void show_stat_info(char *, struct stat *);

int main(int argc, char *argv[]){ 
    struct stat info;
    if(argc != 2){
        printf("usage: ls directory_name\n");
    }
    //do_ls(*argv[1]);

    if(stat(argv[1], &info) != -1){
        show_stat_info(argv[1],&info);
    }
    exit(0);
}

void show_stat_info(char *fname, struct stat *buf)
{
    printf("  mode:  %o\n", buf->st_mode);
    printf(" links:  %d\n", buf->st_nlink);
    printf("  user:  %d\n", buf->st_uid);
    printf("  group: %d\n", buf->st_gid);
    printf("   size: %d\n", buf->st_size);
    printf("modtime: %d\n", buf->st_mtime);
    printf("  group: %s\n", fname);
}

------------------
$./test2 test
  mode:  100755
 links:  1
  user:  501
  group: 20
   size: 12772
modtime: 1578118793
  group: test

实现个V3.0版本

#include<stdio.h>
#include<sys/types.h>
#include<dirent.h>
#include<sys/stat.h>

//代码的报错处理没写好

//列出目录下的所有文件
void do_ls(char[]);
//展示文件信息
void dostat(char *);
//列出文件的信息
void show_stat_info(char *, struct stat *);

int main(int argc, char *argv[]){ 
    if(argc==1)
        do_ls(".");
    do_ls(argv[1]);
        
    return 0;
}

void do_ls(char dirname[])
{
    DIR *dp;
    struct dirent *dirp;

    if((dp=opendir(dirname))==NULL)
        fprintf(stderr,"can't open %s\n",dirname);
    else
    {
        while((dirp=readdir(dp))!=NULL)
            //printf("%s\n",dirp->d_name);
            dostat(dirp->d_name);
        closedir(dp);
    }
}

void dostat(char *filename)
{
    struct stat info;
    if(stat(filename,&info)==-1)
        perror(filename);
    else
        show_stat_info(filename,&info);
}


void show_stat_info(char *fname, struct stat *buf)
{
    printf("  mode:  %o\n", buf->st_mode);
    printf(" links:  %d\n", buf->st_nlink);
    printf("  user:  %d\n", buf->st_uid);
    printf("  group: %d\n", buf->st_gid);
    printf("   size: %d\n", buf->st_size);
    printf("modtime: %d\n", buf->st_mtime);
    printf("  group: %s\n", fname);
}

--------------------
$./test1 ./
  mode:  40700
 links:  63
  user:  501
  group: 20
   size: 2016
modtime: 1578125448
  group: .
  mode:  40755
 links:  145
  user:  501
  group: 20
   size: 4640
modtime: 1578125452
  group: ..
  mode:  40755
 links:  9
  user:  501
  group: 20
   size: 288
modtime: 1575381270
  group: rce-over-spark
  mode:  40755
 links:  7
  user:  501
  group: 20
   size: 224

现在可以写出最终版本了,把文件展示出来的信息修改成与ls一致的信息即可

这里需要补充两个结构体,是将user和group的数字形式转换成name形式

//pwd.h
struct passwd
{
  char *pw_name;                /* Username.  */
  char *pw_passwd;              /* Password.  */
  __uid_t pw_uid;               /* User ID.  */
  __gid_t pw_gid;               /* Group ID.  */
  char *pw_gecos;               /* Real name.  */
  char *pw_dir;                 /* Home directory.  */
  char *pw_shell;               /* Shell program.  */
};
//grp.h
struct group
{
    char *gr_name;              /* Group name.  */
    char *gr_passwd;            /* Password.    */
    __gid_t gr_gid;             /* Group ID.    */
    char **gr_mem;              /* Member list. */
};
#include<stdio.h>
#include<sys/types.h>
#include<dirent.h>
#include<sys/stat.h>
#include <grp.h>
#include <pwd.h>

//代码的报错处理没写好

//列出目录下的所有文件
void do_ls(char[]);
//展示文件信息
void dostat(char *);
//列出文件的信息
void show_file_info(char *, struct stat *);
//将文件属性修改成d/-,权限修改成rwx
void mode_to_letters(int ,char[]);
char * uid_to_name(uid_t);
char * gid_to_name(gid_t);

int main(int argc, char *argv[]){ 
    if(argc==1)
        do_ls(".");
    do_ls(argv[1]);
        
    return 0;
}

void do_ls(char dirname[])
{
    DIR *dp;
    struct dirent *dirp;

    if((dp=opendir(dirname))==NULL)
        fprintf(stderr,"can't open %s\n",dirname);
    else
    {
        while((dirp=readdir(dp))!=NULL)
            //printf("%s\n",dirp->d_name);
            dostat(dirp->d_name);
        closedir(dp);
    }
}

void dostat(char *filename)
{
    struct stat info;
    if(stat(filename,&info)==-1)
        perror(filename);
    else
        show_file_info(filename,&info);
}


void show_file_info(char *filename, struct stat *info_p)
{
    char *uid_to_name(), *ctime(), *gid_to_name();
    void mode_to_letters();
    char modestr[11];

    mode_to_letters(info_p->st_mode,modestr);

    printf("%s",modestr);
    printf("%4d",(int) info_p->st_nlink );
    printf("% -8s", uid_to_name(info_p->st_uid));
    printf("% -8s",gid_to_name(info_p->st_gid));
    printf("%8ld", (long)info_p->st_size);
    printf("%.12s", 4+ctime(&info_p->st_mtime));
    printf("%s\n", filename);
}

void mode_to_letters(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';

    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';
}



char *uid_to_name(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_to_name(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;
}

下一篇用C语言实现Linux的ps命令……

Ref

https://blog.nowcoder.net/n/9a48a7f697864e3bbdae2fe5bfcb0ef9
https://melville.club/posts/a-20190123-095755

Search

    Table of Contents