前言
在Linux系统中,一切皆为文件。
前段时间在Linux课上学习了Linux系统的底层调用,由此可以来通过C/C++来编写自己的Linux命令来对目录文件进行读写。
定制自己的ls命令就是通过命令行参数(不会的自行百度|Google,就是运行时传入的参数),选择选项然后输出某个目录下面的内容。
基础知识
文件描述符
每个打开的文件都被分配一个文件描述符。文件描述符是一个非负整数。linux内核通过文件描述符来访问文件。前三个描述符具有特定含义:
- 0 标准输入(stdin) –键盘
- 1 标准输出(stdout) –显示器
- 2 标准错误(stderr) –默认在终端
- 如果打开的文件没有,那么fd为-1,可由此判断打开是否成功。
- fd的值慢慢增加的,从3开始,因为0,1,2已经被占用。
- 注意文件描述符(int)和文件流(FILE*)之间的区别。
目录dirent结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <sys/types.h> #include <dirent.h> struct dirent{ ino_t d_ino; off_t d_off; unsigned short d_reclen; unsigned char d_type; char d_name[NAME_MAX+1]; } d_ino: 当前目录的inode节点号; d_off:目录文件首部到下个dirent结构的位移; d_reclen:该记录的长度; d_type:目录文件类型; d_name:目录文件名;
|
文件信息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
| #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> struct stat{ dev_t st_dev; ino_t st_ino; mode_t st_mode; nlink_t st_nlink; uid_t st_uid; gid_t st_gid; dev_t st_rdev; off_t st_size; unsigned long st_blksize; unsigned long st_blocks; time_t st_atime; time_t st_mtime; time_t st_ctime; } st_dev:文件所处的磁盘设备ID号; st_ino:文件的inode索引号; st_mode:文件访问权限和文件类型; st_nlink:文件的硬链接数; st_uid:文件所属的用户ID; st_gid:文件所属的组ID; st_rdev:如果文件是字符或块设备,该值给出该设备的标识符信息; st_size:对常规文件,该值为文件大小;对符号链接,该值为该符号链接所指向的目录长度;对设备文件,该值为0; st_blksize:文件系统存储文件的块大小; st_blocks:分配给文件的块数; st_atime:文件的最近访问时间; st_mtime:文件内容的最近修改时间; st_ctime:文件属性的最近修改时间;
|
系统调用
打开关闭读写文件这里仅做记录,在本次内容并没有使用到。
1 2 3 4 5 6 7 8
| #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int open(char *fileName,int mode[,int permissions]) int close(int fd) ssize_t read(int fd, void* buf, size_t count) ssize_t write(int fd, void* buf, size_t count)
|
详细设计及代码
文件结构
1 2 3 4
| - src * main.c * myls.h * myls.c
|
流程
1.在main.c中判断传入的命令行参数是否符合要求。如果没有传入路径根据getwcd(char *buffer,size_t size)函数来获得当前路径。
2.在myls.h中声明两个函数分别为void printdir(char *dir,char option)和void printdetail(struct stat st)来进行调用。
3.printdir():使用全局变量来保存用户选择的选项,是否输出详细信息或者隐藏文件。涉及到的系统调用有:
DIR结构体:保存一个opendir()得到的内容
dirent结构体:存放了目录的信息,有文件名,目录文件类型,节点号和下一个结构的位移等。
4.chdir(dir)切换到该目录下。然后使用readdir(dp)来进行对该目录的循环读取,如果该读到文件是一个隐藏文件,那么判断给出的选项是否需要隐藏(即判断文件名的第一个字母是否则为’.’),还有是否需要输出详细信息的内容。
代码
感觉写的真丑,好久以前的。
1 2 3 4 5 6 7 8 9 10 11
| #include<stdio.h> #include<stdlib.h>
#include<unistd.h> #include<dirent.h> #include<string.h> #include<sys/stat.h>
void printdir(char *dir,char option); void printdetail(struct stat st);
|
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
| #include"myls.h"
int main(int argc,char **argv) { char dir[256]; if(argc==1) { if((getcwd(dir,256))!= NULL) printdir(dir,'n'); else fprintf(stderr,"cannot open current dierectory:%s\n",dir); }else if(argc==2) { if(argv[1][0]=='-') { if((getcwd(dir,256))!= 0) printdir(dir,argv[1][1]); else fprintf(stderr,"cannot open current dierectory:%s\n",dir); }else { printdir(argv[1],'n'); } }else if(argc==3) { printdir(argv[2],argv[1][1]); }else printf("please check your input\n");
}
|
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
| #include"myls.h"
void printdetail(struct stat st) { int i; switch(st.st_mode & S_IFMT) { case S_IFREG: printf("-"); break; case S_IFDIR: printf("d"); break; case S_IFLNK: printf("l"); break; case S_IFBLK: printf("b"); break; case S_IFCHR: printf("c"); break; case S_IFIFO: printf("p"); break; case S_IFSOCK: printf("s"); break; } for(i = 8; i >= 0; i--) { if(st.st_mode & (1 << i)) { switch(i%3) { case 2: printf("r"); break; case 1: printf("w"); break; case 0: printf("x"); break; } } else printf("-"); } }
void printdir(char *dir,char option) { DIR *dp; struct dirent *entry; struct stat statbuf;
if((dp=opendir(dir)) == NULL) { fprintf(stderr,"cannot open directory:%s\n",dir); return; } chdir(dir); while((entry=readdir(dp)) != NULL) { lstat(entry->d_name,&statbuf); if(S_ISDIR(statbuf.st_mode)) { if(entry->d_name[0]=='.') if(option!='a') continue; if(option=='l') { printdetail(statbuf); printf(" %s/\n",entry->d_name); }else printf("%s/ ",entry->d_name); } if(S_ISREG(statbuf.st_mode)) { if(entry->d_name[0]=='.') if(option!='a') continue; if(option=='l') { printdetail(statbuf); printf(" %s\n",entry->d_name);
}else printf("%s ",entry->d_name); } } printf("\n"); chdir(".."); closedir(dp); }
|
编译和运行
使用gcc进行编译之后即可得到一个执行程序,进行执行
1 2 3 4 5 6 7 8 9 10
| $ gcc main.c myls.c -o myls $ ./myls 这样就可以显示当前目录的内容 当然也还可以加入参数,显示home下的内容 $ ./myls /home/ 显示/home/xuan下的详细信息 $ ./myls -a /home/xuan 还可以放到系统命令中的话可以直接调用 $ sudo cp myls /usr/bin $ myls
|
在写了myls的命令之后还写了点其他的命令,比如mydir通过系统调用来递归遍历文件夹之后层次性输出等,方法也都大同小异,也就不过多写了。