【基本功能】
(1)与 ls 命令类似,命令行参数可以有 0 到多个
0 个参数:列出当前目录下所有文件
参数为普通文件:列出文件
参数为目录:列出目录下所有文件
(2)实现自定义选项 r,a,l,h,m 以及–r 递归方式列出子目录
a 列出文件名第一个字符为圆点的普通文件(默认情况下不列出
文件名首字符为圆点的文件)
l 后跟一整数,限定文件大小的最小值(字节)
h 后跟一整数,限定文件大小的最大值(字节)
m 后跟一整数 n,限定文件的最近修改时间必须在 n 天内
– 显式地终止命令选项分析
【程序源码】
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
#include<dirent.h>
#include<sys/stat.h>
char mPath[50];//用户输入的路径
const int _end=5;
const int _r = 0;
const int _a = 1;
const int _l = 2;
const int _h = 3;
const int _m = 4;
int switch_order(int argc, char **argv);//分解参数,成功则返回0,否则返回-1
int execute_order();//执行指令,成功则返回0,否则返回-1
void show_info(const struct dirent *entry,struct stat st);
/***********指令*************
* 共有6个额外的可选指令 *
* -r 递归方式列出子目录
* -a 列出文件名第一个字符为圆点的普通文件
* -l 后跟一整数,限定文件大小的最小值(字节)
* -h 后跟一整数,限定文件大小的最大值(字节)
* -m 后跟一整数n,限定文件的最近修改时间必须在n天内
* --显式地终止命令选项分析
****************************/
int mOrder[6] = { 0 };//指令默认为空
long mMax_size;//最大文件尺寸
long mMin_size;//最小文件尺寸
time_t mDays_limit;//-m的时间限制,另外,time_t其实是long的别名
time_t mCurrent_time;
int main(int argc, char **argv)
{
//DIR *dir;
if(switch_order(argc, argv)==0){//如果转换指令成功
execute_order(mPath);
}
// dir = opendir(argv[1]);
// if (dir == NULL) {
// printf("Open directory \"%s\": %s (ERROR %d)\n",
// argv[1], strerror(errno), errno);
// return 1;
// }
// printf("entry->d_ino\tentry->d_name\tentry->d_type\n" );
// while ((entry = readdir(dir)) != NULL){
// printf("%4d %s %s\n", entry->d_ino, entry->d_name,entry->d_type);
// }
// closedir(dir);
return 0;
}
int switch_order(int argc, char **argv){
time(&mCurrent_time);//获取指令执行时的时间
if (argc == 1) {//目前的指令数目不足(没有参数是1,这里没有参数是不允许的)
printf("the num of the order is not enough\n");
system("pause");
return 1;
}
int i = 0;
int j = 0;
while (i <= argc - 2){//参数紧跟在命令之后,目录在最后面,共有argc-1个参数,减去目录共有argc-2个
if (argv[++i][0] == '-'){//检测到是指令
switch (argv[i][1]){
case 'r':
mOrder[_r] = 1;
break;
case 'a':
mOrder[_a] = 1;
break;
case 'l':
mOrder[_l] = 1;
if ((mMin_size = atol(argv[++i])) == 0){
printf("something wrong with \"-l\" in the order\n");
system("pause");
return 1;
}
break;
case 'h':
mOrder[_h] = 1;
if ((mMax_size = atol(argv[++i])) == 0){
printf("something wrong with \"-h\" in the order\n");
system("pause");
return 1;
}
break;
case 'm':
mOrder[_m] = 1;
if ((mDays_limit = atol(argv[++i])) == 0){
printf("something wrong with \"-m\" in the order\n");
system("pause");
return 1;
}
break;
case '-':
mOrder[_end] = 1;
break;
}
}
else{
//不是-开头则认为是路径
strcpy(mPath, argv[i]);
}
}
printf("path is %s\n",mPath);
return 0;//linux return 0表示运行成功
}
/********************************
执行指令,
如果成功则返回0,失败则返回1
********************************/
int execute_order(const char *path){//传入一个path
int ret;
struct stat st;
struct dirent *entry;//目录里文件的读取指针
char current_path[50];
ret = stat(path, &st);
DIR *dir;
if(ret==0){
if(S_ISDIR(st.st_mode)){
//如果是目录
dir=opendir(path);
if(dir==NULL){
//读取失败
printf("Open dir \"%s\" :%s,ERROR(%d)\n",path,strerror(errno),errno);
return 1;
}
while((entry=readdir(dir))!=NULL){
//这里需要特别注意一下
//entry中会出现当前文件夹和上层文件夹的目录索引 很明显这并不是我们想要的
//所以这里需要特别注意一些
char* current_dir_string=".";
char* parent_dir_string="..";
if(strcmp(entry->d_name,current_dir_string)!=0&&strcmp(entry->d_name,parent_dir_string)!=0){//如果并不是当前目录
//获取当前文件夹下面的各个文件的stat
strcpy(current_path,path);
strcat(current_path,"/");
strcat(current_path,entry->d_name);
ret=stat(current_path,&st);
// printf("%s\n",current_path);
if(ret==0){ //如果成功获取了stat
show_info(entry,st);
if(S_ISDIR(st.st_mode)){//如果当前文件是文件夹
if(mOrder[_r]&&!mOrder[_end]){//如果是递归
execute_order(current_path);
}
}
}
else{//失败则返回原因
printf("Read file \"%s\" :%s,ERROR(%d)\n",path,strerror(errno),errno);
}
}
}
closedir(dir);
}
else{//如果这只是一个文件罢了
printf("%d\t%d\n",st.st_size,st.st_mtime);
}
}
return 0;
}
/*******************
用来对内容进行过滤
*******************/
void show_info(const struct dirent* entry,struct stat st){
//对文件进行选择性过滤
// int count;
// for(count=0;count<6;count++){
// printf("%d : %d\n",count,mOrder[count]);
// }
// printf("%d %d\n",_end,mOrder[_end]);
if(mOrder[_end]==0){
if(mOrder[_a]||entry->d_name[0]!='.'){//默认的情况下以‘.’开头的文件并不会显示出来
if(!mOrder[_l]||st.st_size>mMin_size){
if(!mOrder[_h]||st.st_size<mMax_size){
if(!mOrder[_m]||((mCurrent_time-st.st_mtime)/86400)<mDays_limit){
printf("%d\t%d\t%s\n",st.st_size,st.st_mtime,entry->d_name);
}
}
}
}
}
else{
printf("%d\t%d\t%s\n",st.st_size,st.st_mtime,entry->d_name);
}
}
【运行效果】
<1>基本指令(路径)
<2>进行最大文件过滤
<3>进行最小文件过滤
<4>日期过滤
<5>递归显示所有文件
<6>单个文件测试
<7>综合测试
<8>终止指令测试
【存在的问题】
程序的功能虽然基本实现了,但是程序编写时一些疑惑没有解决。比如,使用了全局变量来保存用户指令,偶尔在编译之后会出现异常。
Const int _i=5在之前的时候命名是const int _end=5。但是非常蹊跷的是,在main函数的开头处打印这个数值,显示的是1!这修改了常量的名称之后又恢复了正常。
(此处怀疑是头文件中的全局变量出现了_end这个常量?但是在我后来的修改中这一问题又消失了)
还有,在show_info()函数中,如果想转换时间戳为字符串格式时。printf(“%d\t%s\t%s\n”,st.st_size,ctime(st.st_mtime),entry->d_name);
在编译过程中不会出错,但是很蹊跷得在运行时显示segment fault。这本是风马牛不相及的错误。
Gcc编译器的调试对我依然很难。第一个问题可能很难再现了。第二个问题希望有人可以和我探讨一下514201942@qq.com