C语言实现Linux的ls命令

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

0x01 基础知识

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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; //该文件所占的磁盘块
};

目录操作相关函数

1
2
3
4
5
6
7
8
9
10
11
//功能:打开一个目录; 返回值:成功则返回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版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#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);
}

封装一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#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结构体可以列出每个文件详细信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#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版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#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形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//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. */
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#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