linux系统编程:自己动手写一个ls命令

时间:2022-06-23 11:17:13

ls用于列举目录内容,要实现这个功能,毫无疑问,需要读取目录,涉及到两个api:

opendir:DIR *opendir(const char *name), 传文件名,返回一个指针,指向目录序列

readdir:struct dirent *readdir(DIR *dirp), 把opendir的返回值传过来,   返回值为一个结构体

        struct dirent {
ino_t d_ino;
/* inode number */
off_t d_off;
/* not an offset; see NOTES */
unsigned
short d_reclen; /* length of this record */
unsigned
char d_type; /* type of file; not supported
by all filesystem types
*/
char d_name[256]; /* filename */
};

有了这两个api,就可以实现一个简易的ls功能

linux系统编程:自己动手写一个ls命令linux系统编程:自己动手写一个ls命令
 1 /*================================================================
2 * Copyright (C) 2018 . All rights reserved.
3 *
4 * 文件名称:myls.c
5 * 创 建 者:ghostwu(吴华)
6 * 创建日期:2018年01月09日
7 * 描 述: ls命令
8 *
9 ================================================================*/
10
11 #include <stdio.h>
12 #include <sys/types.h>
13 #include <dirent.h>
14 #include <stdlib.h>
15
16 void do_ls( char [] );
17
18 int main(int argc, char *argv[])
19 {
20 if( argc == 1 ) {
21 do_ls( "." );
22 }else {
23 while( --argc ) {
24 printf( "arg=%s\n", * ++argv );
25 do_ls( *argv );
26 }
27 }
28 return 0;
29 }
30
31 void do_ls( char dir_entry[] ) {
32 DIR* pDir;
33 struct dirent* pCurDir;
34 if( ( pDir = opendir( dir_entry ) ) == NULL ){
35 perror( "read dir" );
36 exit( -1 );
37 }else {
38 while( ( pCurDir = readdir( pDir ) ) != NULL ) {
39 printf( "%s\n", pCurDir->d_name );
40 }
41 }
42 }
View Code

这个简易的ls功能,列举出了所有的文件( 包括隐藏的 ), 但是很多的信息不全,如: 权限,用户和组,修改时间,文件大小,链接数目等,stat这个api可以获取文件的这些信息

stat:获取文件状态信息

原型:int stat(const char *pathname, struct stat *buf), 第一个参数:文件名, 第二个参数:保存文件状态信息的结构体( man 2 stat 有结构体相关说明 )

1、获取文件的大小

linux系统编程:自己动手写一个ls命令linux系统编程:自己动手写一个ls命令
 1 /*================================================================
2 * Copyright (C) 2018 . All rights reserved.
3 *
4 * 文件名称:stat.c
5 * 创 建 者:ghostwu(吴华)
6 * 创建日期:2018年01月09日
7 * 描 述:
8 *
9 ================================================================*/
10
11 #include <stdio.h>
12 #include <sys/stat.h>
13
14 #define FILENAME "/etc/passwd"
15
16 int main(int argc, char *argv[])
17 {
18 struct stat filestat;
19
20 if( -1 == stat( FILENAME, &filestat ) ) {
21 perror( "file stat" );
22 return -1;
23 }else {
24 printf( "the size of %s is %ld\n",FILENAME, filestat.st_size );
25 }
26
27 return 0;
28 }
View Code

2、读取文件st_mode(权限位), 用户id, 组id, 修改时间,链接数目

linux系统编程:自己动手写一个ls命令linux系统编程:自己动手写一个ls命令
 1 /*================================================================
2 * Copyright (C) 2018 . All rights reserved.
3 *
4 * 文件名称:stat2.c
5 * 创 建 者:ghostwu(吴华)
6 * 创建日期:2018年01月09日
7 * 描 述:
8 *
9 ================================================================*/
10
11 #include <stdio.h>
12 #include <sys/stat.h>
13 #include <string.h>
14 #include <time.h>
15
16 void show_info( char *file, struct stat* statinfo );
17 void show_time( time_t filetime );
18 char* format_time( char* dsttime, const char* srctime );
19
20 int main(int argc, char *argv[])
21 {
22 struct stat fileinfo;
23 if( argc > 1 ) {
24 /*调试信息
25 printf( "%s\n", argv[1] );
26 int res = stat( argv[1], &fileinfo );
27 printf( "%d\n", res );
28 */
29 if( stat( argv[1], &fileinfo ) != -1 ) {
30 show_info( argv[1], &fileinfo );
31 }
32 }else {
33 perror( "get args from terminal" );
34 }
35
36 return 0;
37 }
38
39 void show_info( char* file, struct stat* statinfo ){
40 printf( "%s文件信息如下:\n", file );
41 printf( "st_mode = %d\n", statinfo->st_mode );
42 printf( "links = %ld\n", statinfo->st_nlink );
43 printf( "uid = %d\n", statinfo->st_uid );
44 printf( "gid = %d\n", statinfo->st_gid );
45 printf( "file size = %ld\n", statinfo->st_size );
46 show_time( statinfo->st_mtime );
47 }
48
49 void show_time( time_t filetime ) {
50 struct tm* ptm;
51 ptm = localtime( &filetime );
52
53 int month = ptm->tm_mon + 1;
54 int day = ptm->tm_mday;
55 int hour = ptm->tm_hour;
56 int min = ptm->tm_min;
57
58 char srchour[3] = "0";
59 char srcmin[3] = "0";
60 char dsthour[3] = "0";
61 char dstmin[3] = "0";
62 sprintf( srchour, "%d", hour );
63 sprintf( srcmin, "%d", min );
64 format_time( dsthour, srchour );
65 format_time( dstmin, srcmin );
66
67 printf( "文件最后修改时间: %d月\t%d\t%s:%s\n", month, day, dsthour, dstmin );
68 }
69
70 char* format_time( char* dsttime, const char* srctime ) {
71 if( strlen( srctime ) < 2 ) {
72 return strcat( dsttime, srctime );
73 }
74 return strcpy( dsttime, srctime );
75 }
View Code

3、权限st_mode转字符权限位( 如: -rwxrwxrwx ), 用户id和组id转用户名和组名称,判断文件类型

linux系统编程:自己动手写一个ls命令linux系统编程:自己动手写一个ls命令
  1 /*================================================================
2 * Copyright (C) 2018 . All rights reserved.
3 *
4 * 文件名称:stat3.c
5 * 创 建 者:ghostwu(吴华)
6 * 创建日期:2018年01月09日
7 * 描 述:文件类型与权限位
8 *
9 ================================================================*/
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <sys/stat.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <pwd.h>
17 #include <grp.h>
18
19 void do_ls( char* filename );
20 void show_filetype( char* filename, int filemode );
21 void show_filetype2( char* filename, int filemode );
22 void mode_to_letters( int filemode, char str[] );
23 //用户id转名称
24 char* uid_to_name( uid_t uid );
25 //组id转名称
26 char* gid_to_name( gid_t gid );
27
28 int main(int argc, char *argv[])
29 {
30 if( argc < 2 ) {
31 printf( "usage:%s file\n", argv[0] );
32 return -1;
33 }else {
34 do_ls( argv[1] );
35 }
36 return 0;
37 }
38
39 char* uid_to_name( uid_t uid ){
40 return getpwuid( uid )->pw_name;
41 }
42
43 char* gid_to_name( gid_t gid ){
44 return getgrgid( gid )->gr_name;
45 }
46
47 void do_ls( char* filename ) {
48 struct stat fileinfo;
49 if( stat( filename, &fileinfo ) == -1 ) {
50 printf( "%s open failure\n", filename );
51 exit( -1 );
52 }
53 //printf( "st_mode = %d\n", fileinfo.st_mode );
54 show_filetype( filename, fileinfo.st_mode );
55 show_filetype2( filename, fileinfo.st_mode );
56 char file_permission[10];
57 mode_to_letters( fileinfo.st_mode, file_permission );
58 printf( "%s\n", file_permission );
59 printf( "用户:%s\n", uid_to_name( fileinfo.st_uid ) );
60 printf( "组:%s\n", gid_to_name( fileinfo.st_gid ) );
61 }
62
63 //掩码判断文件类型
64 void show_filetype( char* filename, int filemode ){
65 //用st_mode的值跟0170000这个掩码相位与的结果 判断文件类型
66 if ( ( filemode & 0170000 ) == 0100000 ){
67 printf( "%s是普通文件\n", filename );
68 }else if( ( filemode & 0170000 ) == 0040000 ){
69 printf( "%s是目录\n", filename );
70 }else if ( ( filemode & S_IFMT ) == S_IFLNK ){
71 printf( "%s是符号链接\n", filename );
72 }
73 }
74
75 //用宏判断文件类型
76 void show_filetype2( char* filename, int filemode ){
77 if( S_ISREG( filemode ) ) {
78 printf( "%s是普通文件\n", filename );
79 }else if( S_ISDIR( filemode ) ) {
80 printf( "%s是目录\n", filename );
81 }else if( S_ISLNK( filemode ) ){
82 printf( "%s是符号链接\n", filename );
83 }
84 }
85
86 //数字解码成字母权限位
87 void mode_to_letters( int filemode, char str[] ) {
88 strcpy( str, "----------" );
89 if( S_ISREG( filemode ) ) str[0] = '-';
90 if( S_ISDIR( filemode ) ) str[0] = 'd';
91 if( S_ISLNK( filemode ) ) str[0] = 'l';
92
93 //用户权限位
94 if( filemode & S_IRUSR ) str[1] = 'r';
95 if( filemode & S_IWUSR ) str[2] = 'w';
96 if( filemode & S_IXUSR ) str[3] = 'x';
97
98 //组权限位
99 if( filemode & S_IRGRP ) str[4] = 'r';
100 if( filemode & S_IWGRP ) str[5] = 'w';
101 if( filemode & S_IXGRP ) str[6] = 'x';
102
103 //其他组权限位
104 if( filemode & S_IROTH ) str[7] = 'r';
105 if( filemode & S_IWOTH ) str[8] = 'w';
106 if( filemode & S_IXOTH ) str[9] = 'x';
107 }
View Code

综合上面3个小实例,可以得到格式化比较好的ls命令版本:

linux系统编程:自己动手写一个ls命令linux系统编程:自己动手写一个ls命令
  1 /*================================================================
2 * Copyright (C) 2018 . All rights reserved.
3 *
4 * 文件名称:myls2.c
5 * 创 建 者:ghostwu(吴华)
6 * 创建日期:2018年01月09日
7 * 描 述:ls命令( version 1.2 )
8 *
9 ================================================================*/
10
11 #include <stdio.h>
12 #include <sys/types.h>
13 #include <dirent.h>
14 #include <stdlib.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18 #include <string.h>
19 #include <sys/types.h>
20 #include <pwd.h>
21 #include <grp.h>
22 #include <time.h>
23
24 void do_ls( char [] );
25 void do_stat( char* filename );
26 void show_list( char* filename, struct stat* statinfo );
27 void mode_to_letters( mode_t filemode, char str[] );
28 void show_time( time_t filetime );
29 char* format_time( char* dsttime, const char* srctime );
30
31 //用户id转名称
32 char* uid_to_name( uid_t uid );
33 //组id转名称
34 char* gid_to_name( gid_t gid );
35
36 int main(int argc, char *argv[])
37 {
38 if( argc == 1 ) {
39 do_ls( "." );
40 }else {
41 while( --argc ) {
42 printf( "arg=%s\n", * ++argv );
43 do_ls( *argv );
44 }
45 }
46 return 0;
47 }
48
49 void do_ls( char dir_entry[] ) {
50 DIR* pDir;
51 struct dirent* pCurDir;
52 if( ( pDir = opendir( dir_entry ) ) == NULL ){
53 perror( "read dir" );
54 exit( -1 );
55 }else {
56 while( ( pCurDir = readdir( pDir ) ) != NULL ) {
57 do_stat( pCurDir->d_name );
58 }
59 closedir( pDir );
60 }
61 }
62
63 //得到文件信息
64 void do_stat( char* filename ){
65 struct stat statinfo;
66 if ( stat( filename, &statinfo ) == -1 ) {
67 printf( "打开%s失败\n", filename );
68 exit( -1 );
69 }else {
70 show_list( filename, &statinfo );
71 }
72 }
73
74 //显示文件列表
75 void show_list( char* filename, struct stat* statinfo ) {
76 mode_t st_mode = statinfo->st_mode;
77
78 char str[10];
79 mode_to_letters( st_mode, str );
80 printf( "%s\t", str );
81
82 printf( "%ld\t", statinfo->st_nlink ); //符号链接
83 printf( "%s\t\t", uid_to_name( statinfo->st_uid ) ); //用户名
84 printf( "%s\t", gid_to_name( statinfo->st_gid ) ); //组名
85 printf( "%10ld", statinfo->st_size ); //文件大小
86 show_time( statinfo->st_mtime ); //最后一次修改时间
87 printf( "\t%s", filename );
88
89 printf( "\n" );
90 }
91
92 char* uid_to_name( uid_t uid ){
93 return getpwuid( uid )->pw_name;
94 }
95
96 char* gid_to_name( gid_t gid ){
97 return getgrgid( gid )->gr_name;
98 }
99
100 void mode_to_letters( mode_t filemode, char str[] ) {
101
102 strcpy( str, "----------" );
103 if( S_ISREG( filemode ) ) str[0] = '-';
104 if( S_ISDIR( filemode ) ) str[0] = 'd';
105 if( S_ISLNK( filemode ) ) str[0] = 'l';
106
107 //用户权限位
108 if( filemode & S_IRUSR ) str[1] = 'r';
109 if( filemode & S_IWUSR ) str[2] = 'w';
110 if( filemode & S_IXUSR ) str[3] = 'x';
111
112 //组权限位
113 if( filemode & S_IRGRP ) str[4] = 'r';
114 if( filemode & S_IWGRP ) str[5] = 'w';
115 if( filemode & S_IXGRP ) str[6] = 'x';
116
117 //其他组权限位
118 if( filemode & S_IROTH ) str[7] = 'r';
119 if( filemode & S_IWOTH ) str[8] = 'w';
120 if( filemode & S_IXOTH ) str[9] = 'x';
121 }
122
123 void show_time( time_t filetime ) {
124 struct tm* ptm;
125 ptm = localtime( &filetime );
126
127 int month = ptm->tm_mon + 1;
128 int day = ptm->tm_mday;
129 int hour = ptm->tm_hour;
130 int min = ptm->tm_min;
131
132 char srchour[3] = "0";
133 char srcmin[3] = "0";
134 char dsthour[3] = "0";
135 char dstmin[3] = "0";
136 sprintf( srchour, "%d", hour );
137 sprintf( srcmin, "%d", min );
138 format_time( dsthour, srchour );
139 format_time( dstmin, srcmin );
140
141 printf( "%4d月%4d%4s:%2s", month, day, dsthour, dstmin );
142 }
143
144 char* format_time( char* dsttime, const char* srctime ) {
145 if( strlen( srctime ) < 2 ) {
146 return strcat( dsttime, srctime );
147 }
148 return strcpy( dsttime, srctime );
149 }
View Code

 

总结:

1)opendir和readdir的用法

2)结构体struct dirent的应用

3)stat的用法