欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

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

程序员文章站 2022-06-23 19:18:39
ls用于列举目录内容,要实现这个功能,毫无疑问,需要读取目录,涉及到两个api: opendir:DIR *opendir(const char *name), 传文件名,返回一个指针,指向目录序列 readdir:struct dirent *readdir(DIR *dirp), 把opendi ......
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命令
 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命令
 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命令
 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命令
  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命令
  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的用法