简单实现的dos命令
CLS, DATE,TIME,FIND,FINDSTR,COMP,FC,EXIT,HELP,MORE
说明
- 由于自己能力和时间有限,程序依旧存在不少bug,并且不是原模原样的实现dos命令,有的简单实现,有的命令参数众多,只实现了几个
- 这个程序写的并不优雅,违反了很多大忌,只是简单能跑
- 写完后我再也不想见到“段错误”这三个字,心酸,这个可能在出现段错误时为你提供点解决思路https://www.cnblogs.com/zl-graduate/p/5735288.html
- 使用的环境
- gcc 9.2.1 20190909
- GNU gdb (Debian 8.3-1) 8.3
- Kali-Linux-2018.2-vm-amd64
- 参考的Linux api文档
结构
为了演示,所用到的两个文档1.txt 2.txt
CLS
功能
cls命令的功能是清屏
设计流程
其实就是简单地 fputs("\x1b[2J\x1b[H", stdout);其中的不明所以的字符串是VT100的控制码,部分定义如下 "\x1b[2J"清除整个屏幕,行属性变成单宽单高,光标位置不变 "\x1b[H"光标移动 \033[0m 关闭所有属性 \033[1m 设置为高亮 \033[4m 下划线 \033[5m 闪烁 \033[7m 反显 \033[8m 消隐 \033[nA 光标上移 n 行 \033[nB 光标下移 n 行 \033[nC 光标右移 n 行 \033[nD 光标左移 n 行 \033[y;xH 设置光标位置 \033[2J 清屏 \033[K 清除从光标到行尾的内容 \033[s 保存光标位置 \033[u 恢复光标位置 \033[?25l 隐藏光标 \033[?25h 显示光标 \033[30m – \033[37m 设置前景色
效果
DATE
功能
date 命令用来查看和修改当前日期 详细操作查看https://jingyan.baidu.com/article/1974b2893e7d62f4b1f774ff.html
设计流程
判断是否有输入参数t,?,进行相关处理。 如果没有则打印当前日期,并提示输入更改日期,判断日期是否合法再进行修改 判断年月日是否合法直接用了这位博主的代码 https://blog.csdn.net/freeape/article/details/48682411
效果
TIME
功能
time 用来查看和修改计算机时间 详细操作查看https://jingyan.baidu.com/article/aa6a2c14ab5ac90d4c19c492.html
设计流程
判断是否有输入参数t,?,进行相关处理。 如果没有则打印当前时间,并提示输入更改时间,判断时间是否合法再进行修改
效果
FIND
在之后涉及比较的命令的程序都参考了https://zhidao.baidu.com/question/237942227.html下一骑当后的回答
功能
find 命令用于查找文档中的特定字符和数字及行号 详细操作查看https://jingyan.baidu.com/article/86f4a73ecfe08637d65269ef.html
设计流程
采用指针遍历文件的匹配算法来寻找字符串,并通过输入参数/C/N/V等控制输出打印的形式
效果
FINDSTR
这个命令自己实现的并不完全
功能
findstr是find的升级版 详细操作查看https://jingyan.baidu.com/article/a65957f418641e24e67f9b99.html
设计流程
命令所需参数有文件路径,文件类型和所搜寻的字符串 该模块包含两个函数 findstr函数如上图1,主要负责遍历目录与文件,挑选符合参数决定的文件类型 find1函数如上图2,主要负责采用指针遍历文件,与参数决定的字符串进行对比,找到所匹配的字符串。每次读取一行采用指针遍历方法与字符串进行对比 对于文件的遍历和文件类型的识别参考了 https://zhidao.baidu.com/question/683010229831852652.html https://www.cnblogs.com/xudong-bupt/p/3504442.html 以下是我的程序
while ((ptr = readdir(dir)) != NULL) { if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) //是当前目录和父目录 continue; else if (ptr->d_type == 4) //是dir { char *cpath = (char *)malloc(strlen(path) + strlen(ptr->d_name) + strlen("/") + 1); strcpy(cpath, path); strcat(cpath, "/"); strcat(cpath, ptr->d_name); //findstr(cpath, type, str); } else//是文件 { p = strrchr(ptr->d_name, '.');//取得文件最后一个.后的字符,即文件的类型 sprintf(ftype, "%s", p); if (strcmp(ftype, type) == 0) { char *fpath = (char *)malloc(strlen(path) + strlen(ptr->d_name) + strlen("/") + 1); strcpy(fpath, path); strcat(fpath, "/"); strcat(fpath, ptr->d_name); find1(fpath, str); } } }
使用readdir()返回一个结构
struct dirent { ino_t d_ino; ff_t d_off; signed short int d_reclen; unsigned char d_type; har d_name[256]; };
d_ino 此目录进入点的inode d_off 目录文件开头至此目录进入点的位移 d_reclen _name的长度,不包含NULL字符 d_type 所指的文件类型 d_name 文件名
效果
COMP
该命令要求两个文件的大小相同,获取文件大小的方法参考了https://www.cnblogs.com/xudong-bupt/p/3506772.html
功能
comp逐字节比较两个文件或文件集的内容 详细操作查看https://jingyan.baidu.com/article/90808022da9bbbfd91c80f1b.html](https://jingyan.baidu.com/article/90808022da9bbbfd91c80f1b.html)
设计流程
主要包含三个函数comp,command_comp,file_comp分别对应上图1,2,3 comp主要通过控制流程控制程序走向 command_comp主要对输入的参数进行分析,并返回一个和(包含参数信息,/L对应1,/A对应2,/D对应4,无对应0) file_comp主要通过匹配算法进行字符串的查找,并根据对参数指令的分析返回的值进行打印输出
效果
FC
功能
FC是DOS及Windows下的一个比较文件的命令行工具,使用该命令能够将两个类似文件的不同之处进行详细对比。 详细操作查看https://baike.baidu.com/item/FC/10362340?fr=aladdin
设计流程
比较两个文件的不同之处,循环读取文件1的一行,与文件2的每一行进行对比,输出不相等的行。再循环读取文件2的一行,与文件1的每一行进行对比,输出不相等的行。
效果
EXIT
功能
退出当前命令窗口 详细操作查看https://baike.baidu.com/item/FC/10362340?fr=aladdin
设计流程
这里实在不会实现,只好假装实现,因为通过命令窗口调用该程序,所以得到该程序的父进程的id,然后将其kill假装实现了exit
效果
HELP
功能
help列出命令的帮助信息 详细操作查看https://jingyan.baidu.com/article/ea24bc39d29264da63b33163.html
设计流程
直接输入help命令显示所有help信息,通过help+命令,输出对应命令的信息
效果
MORE
功能
more 命令将文本文档逐行进行显示, 也可显示多个文档, 跳行和显示下几行 详细操作查看https://jingyan.baidu.com/article/bad08e1e327c3709c85121fa.html 其实这个程序大部分用了github上的代码,可惜后面找不到出处了
设计流程
主要包含more see_more do_more几个函数,这里全部展示在上图。 more允许打印多个文件,当打印满时,可以通过命令q退出命令,命令m打印更多,命令\n打印下一个文件。
效果
两个文件时 M继续显示,\n显示下一个文件
主函数
设计流程
效果
为了模仿windows前面的提示符,通过getcwd()获取当前目录,并显示
其它说明
对于格式化显示输出的问题
保留小数https://blog.csdn.net/qq_36667170/article/details/79265224 前面补0https://zhidao.baidu.com/question/2272528818662923828.html
动态开辟多个指针
参考自https://zhidao.baidu.com/question/1430108991238952819.html
pArrStr=(char**)malloc(sizeof(char*)*strLen);//动态开辟N个char*指针,然后给pArrStr保存 for(i=0;i<strLen;i++) { pArrStr[i]=(char*)malloc(255); }
对时间相关模块说明
其实开始说明中的linux api文档中解释的就可以,并且有相关示例,这个也比较详细https://blog.csdn.net/lhl_blog/article/details/86238140
相关调用函数清单
struct tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; int tm_isdst; }; int tm_sec 代表目前秒数,正常范围为0-59,但允许至61秒 int tm_min 代表目前分数,范围0-59 int tm_hour 从午夜算起的时数,范围为0-23 int tm_mday 目前月份的日数,范围01-31int tm_mon 代表目前月份,从一月算起,范围从0-11 int tm_year 从1900 年算起至今的年数int tm_wday 一星期的日数,从星期一算起,范围为0-6 int tm_yday 从今年1月1日算起至今的天数,范围为0-365 int tm_isdst 日光节约时间的旗标 此函数返回的时间日期未经时区转换,而是UTC时间。
struct timeval{ long tv_sec; /*秒*/ long tv_usec; /*微秒*/ };
struct timezone{ int tz_minuteswest; /*和Greenwich 时间差了多少分钟*/ int tz_dsttime; /*日光节约时间的状态*/ };
time_t time(time_t *t); 返回从公元1970年1月1日的UTC时间从0时0分0秒算起到现在所经过的秒数。 struct tm *localtime(const time_t *timep); 将参数timep所指的time_t结构中的信息转换成真实世界所使用的时间日期表示方法,然后将结果由结构tm返回。 time_t mktime(strcut tm *timeptr); mktime()用来将参数timeptr所指的tm结构数据转换成从公元1970年1月1日0时0分0 秒算起至今的UTC时间所经过的秒数。 int stime(long *tp)设置时间。 int settimeofday(const struct timeval *tv, const struct timezone *tz);设置当前时间。
程序涉及到了很多字符串的操作
字符串和数字的转化
https://blog.csdn.net/smile_zhangw/article/details/82051014
字符串的拼接
https://www.cnblogs.com/metaphors/p/9409153.html
相关调用函数清单
int strcmp(const char *s1, const char *s2);比较两个字符串。 char *strtok(char *s, const char *delim); strtok()用来将字符串分割成一个个片段。参数s指向欲分割的字符串,参数delim则为分割字符串,当strtok()在参数s的字符串中发现到参数delim的分割字符时则会将该字符改为\0 字符。在第一次调用时,strtok()必需给予参数s字符串,往后的调用则将参数s设置成NULL。每次调用成功则返回下一个分割后的字符串指针。 在main()中的使用 //对命令按空格进行分割,命令存入*argv[]中,命令数存入argc中 if (strlen(input) != 0) { char *delim = " "; char *p; p = strtok(input, delim); argv[0] = p; while ((p = strtok(NULL, delim))) { argv[argc] = p; argc++; } } char *gets(char *s); 由标准输入设备内读进一字符串。 char *strcpy(char *dest, const char *src); strcpy()会将参数src字符串拷贝至参数dest所指的地址。 char *strcat(char *dest, const char *src);连接两个字符串。 char *strchr(const char *s, int c); 查找字符串中第一个出现的指定字符。 size_t strlen(const char *s);返回字符串长度
对涉及文件相关操作模块说明
清空缓存
因为c读取字符时,可能读到缓冲区的字符,产生影响,所以需要清除 参考https://jingyan.baidu.com/article/9f7e7ec0b5e4a86f28155415.html https://zhuanlan.zhihu.com/p/54990226
相关调用函数清单
FILE *fopen(const char *path, const char *mode); 参数path字符串包含欲打开的文件路径及文件名,参数mode字符串则代表着流形态。 char *fgets(char *s, int size, FILE *stream);从文件中读取一字符串。 long ftell(FILE *stream);取得文件流的读取位置。 struct dirent *readdir(DIR *dir);读取目录。 int fseek(FILE *stream, long offset, int whence);移动文件流的读写位置。 int fputs(const char *s, FILE *stream);将一指定字符写入文件内。 int fgetc(FILE * stream);文件中读取一个字符。 int getchar(void); getchar()用来从标准输入设备中读取一个字符。然后将该字符从unsigned char转换成int后返回。
程序
gcc -o shell shell.c ./shell
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h> #include <sys/types.h> #include <string.h> #include <time.h> #include <stdbool.h> #include <sys/time.h> #include <time.h> #include <stdbool.h> #include <sys/types.h> #include <dirent.h> #include <unistd.h> //findstr命令 void find1(char *argv, char *str) { FILE *fp; int stringsize = strlen(str) * sizeof(char); char *line = (char *)calloc(512, sizeof(char)); fp = fopen(argv, "r"); int i = 0; while (fgets(line, 512, fp)) { int j = 0; char *start; i = 0; start = line; while (i < ftell(fp) - stringsize) { j = 0; while (*line) { if (*str == *line) { str++; line++; j++; continue; } break; } if (j == stringsize) { str = str - j; printf("%s", start); break; } else { str = str - j; line = line - j + 1; } i++; } } } void findstr(char *path, char *type, char *str) { DIR *dir; struct dirent *ptr; char *ftype = (char *)malloc(512); char *p; dir = opendir(path); while ((ptr = readdir(dir)) != NULL) { if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) //是当前目录和父目录 continue; else if (ptr->d_type == 4) //是dir { char *cpath = (char *)malloc(strlen(path) + strlen(ptr->d_name) + strlen("/") + 1); strcpy(cpath, path); strcat(cpath, "/"); strcat(cpath, ptr->d_name); //findstr(cpath, type, str); } else//是文件 { p = strrchr(ptr->d_name, '.');//取得文件最后一个.后的字符,即文件的类型 sprintf(ftype, "%s", p); if (strcmp(ftype, type) == 0) { char *fpath = (char *)malloc(strlen(path) + strlen(ptr->d_name) + strlen("/") + 1); strcpy(fpath, path); strcat(fpath, "/"); strcat(fpath, ptr->d_name); find1(fpath, str); } } } } //find命令 int command_comp1(char **str) { int i = 3; if (strcmp(str[i], "/V") == 0)//包含字符串的所在行,不显示 { return 1; } else if (strcmp(str[i], "/C") == 0)//只显示包含字符串的行 { return 2; } else if (strcmp(str[i], "/N") == 0)//显示找到的包含字符串的行数 { return 3; } else { printf("命令行参数有误"); exit(0); } } void find(int argc, char **argv) { FILE *fp; char *string = argv[2]; int stringsize = strlen(string) * sizeof(char); char *line = (char *)calloc(512, sizeof(char)); fp = fopen(argv[1], "r"); int i = 0, flag = 0, linec = 1, linen = 0; int pa; pa = command_comp1(argv); while (fgets(line, 512, fp))//读取一行 { int j = 0; char *start; i = 0; start = line; while (i < ftell(fp) - stringsize)//将一行的内容与字符串进行比较 { j = 0; while (*line) { if (*string == *line) { string++; line++; j++; continue; } break; } if (j == stringsize)//比较成功 { flag = 1; linen++; string = string - j; } else//比较失败,回退 { string = string - j; line = line - j + 1; } i++; } if (pa == 1) { if (flag != 1) { printf("%s", start); } else { printf("\n"); } } else if (pa == 3) { if (flag == 1) { printf("%d\t", linec); printf("%s", start); } } linec++; flag = 0; } if (pa == 2 && linen != 0) { printf("%d\n", linen); } } //comp命令 long fsize(FILE *f)//比较两个文件的大小 { long size; fseek(f, 0, SEEK_END); ///将文件指针移动文件结尾 size = ftell(f); ///求出当前文件指针距离文件开始的字节数 return size; } int command_comp(char **str, int c) { int flag = 0; int i = 3; if (i == c) { return flag; } for (i; i < c; i++) { if (strcmp(str[i], "/L") == 0)//显示行号 { flag = flag + 1; } else if (strcmp(str[i], "/A") == 0)//显示char型 { if (flag == 1 || flag == 0) { flag = flag + 2; } } else if (strcmp(str[i], "/D") == 0)//显示十进制 { if (flag == 1 || flag == 0) { flag = flag + 4; } } else if (strcmp(str[i], "q") == 0) { } else { printf("命令行参数有误"); exit(0); } } return flag; } void file_comp(char *file1, char *file2, int para) { FILE *f1; FILE *f2; char char1, char2; int offset = 0; int line = 1; f1 = fopen(file1, "r"); f2 = fopen(file2, "r"); if (fsize(f1) != fsize(f2))//比较文件大小 { printf("文件的大小不同"); } else { fseek(f1, 0, SEEK_SET); fseek(f2, 0, SEEK_SET); char1 = fgetc(f1); char2 = fgetc(f2); offset++; if (char1 == '\n') { line++; } while (char1 != EOF) { if (char1 != char2) { if (para == 0) { printf("在offset %d比较错误\n", offset); printf("file1=%x\n", char1); printf("file2=%x\n", char2); } else if (para == 1) { printf("在line %d比较错误\n", line); printf("file1=%x\n", char1); printf("file2=%x\n", char2); } else if (para == 2) { printf("在offset %d比较错误\n", offset); printf("file1=%c\n", char1); printf("file2=%c\n", char2); } else if (para == 4) { printf("在offset %d比较错误\n", offset); printf("file1=%d\n", (int)char1); printf("file2=%d\n", (int)char2); } else if (para == 3) { printf("在line %d比较错误\n", line); printf("file1=%c\n", char1); printf("file2=%c\n", char2); } else { printf("在line %d比较错误\n", line); printf("file1=%d\n", char1); printf("file2=%d\n", char2); } } char1 = fgetc(f1); char2 = fgetc(f2); offset++; if (char1 == '\n') { line++; } } } } void comp(int ac, char **av) { int pa; char **command; int strLen = 10; command = (char **)malloc(sizeof(char *) * strLen); //动态开辟多个char*指针 for (int i = 0; i < strLen; i++) { command[i] = (char *)malloc(255); } command[0] = "0"; if (ac == 1) { char *f1; char *f2; int i = 1; char line1[512]; char line2[512]; printf("第一个比较文件的名称"); scanf("%s", command[i]); i++; printf("第二个比较文件的名称"); scanf("%s", command[i]); while (strcmp(command[i], "q") != 0) { i++; printf("选项"); scanf("%s", command[i]); } pa = command_comp(command, i);//分析参数 file_comp(command[1], command[2], pa);//字符查找 } else { pa = command_comp(av, ac); file_comp(av[1], av[2], pa); } } //fc命令 void fc(char **argv) { FILE *fp1; FILE *fp2; fp1 = fopen(argv[1], "r"); fp2 = fopen(argv[2], "r"); char line1[512]; char line2[512]; int flag = 0; printf("正在比较文件 %s 和 %s\n", argv[1], argv[2]); printf("****%s\n", argv[1]); while (fgets(line1, 512, fp1))//读取文件1的一行,与文件2的每一行相比较 { flag = 0; while (fgets(line2, 512, fp2)) { if (strcmp(line1, line2) == 0) { flag = 1; break; } } if (flag == 0) { fputs(line1, stdout); } fseek(fp2, 0, SEEK_SET);//文件指针指向开头 } fseek(fp1, 0, SEEK_SET); printf("****%s\n", argv[2]); while (fgets(line2, 512, fp2))//读取文件2的一行,与文件1的每一行相比较 { flag = 0; while (fgets(line1, 512, fp1)) { if (strcmp(line1, line2) == 0) { flag = 1; break; } } if (flag == 0) { fputs(line2, stdout); } fseek(fp1, 0, SEEK_SET); } if (flag == 1) { printf("找不到差异"); } } /more命令 int see_more() { int c; printf("\033[7m more? \033[0m"); // : \033[7m printf反显 c = getchar(); if (c != EOF) { if (c == 'q')//退出 return 0; if (c == 'm')//继续显示 return 24; if (c == '\n')//显示下一个文件 return 1; } return 0; } void do_more(FILE *fp) { char line[512]; int num_of_line = 0; int reply; //读取每一行赋值给line while (fgets(line, 512, fp)) { if (num_of_line == 24) { //满屏 reply = see_more(); if (reply == 0) break; num_of_line -= reply; } if (fputs(line, stdout) == EOF) //打印 break; num_of_line++; } } void more(int argc, char **argv) { FILE *fp; if (argc == 1) { do_more(stdin); } else { while (--argc) { if ((fp = fopen(*++argv, "r")) != NULL) { do_more(fp); fclose(fp); } else break; } } } //help命令 void help(int argc, char **argv) { int n = 0; if (argc == 1) { printf("CLS 清除屏幕。\n"); printf("COMP 比较两个或两套文件的内容。\n"); printf("DATE 显示或设置日期。\n"); printf("DOSKEY 编辑命令行、撤回 Windows 命令并创建宏。\n"); printf("EXIT 退出 CMD.EXE 程序(命令解释程序)。 \n"); printf("FC 比较两个文件或两个文件集并显示它们之间的不同。\n"); printf("FIND 在一个或多个文件中搜索一个文本字符串。\n"); printf("FINDSTR 在多个文件中搜索字符串。\n"); printf("HELP 提供 Windows 命令的帮助信息。\n"); printf("MORE 逐屏显示输出。\n"); printf("TIME 显示或设置系统时间。\n"); } else { if (strcmp(argv[1], "cls") == 0) { printf("清除屏幕。\n\nCLS\n"); n = 1; } if (strcmp(argv[1], "comp") == 0) { printf("比较两个文件或两个文件集的内容。\n\nCOMP [data1] [data2] [/D] [/A] [/L] [/N=number] [/C] [/OFF[LINE]] [/M]\n data1 指定要比较的第一批文件的位置和名称。\ndata2 指定要比较的第二批文件的位置和名称。\n/D 以十进制格式显示差异。\n /A 以 ASCII 字符显示差异。\n /L 显示不同的行数。\n/N=number 只比较每个文件中第一个指定的行数。\n/C 比较文件时 ASCII 字母不区分大小写。\n/OFF[LINE] 不要跳过带有脱机属性集的文件。\n/M 不提示比较更多文件。\n\n要比较文件集,请在 data1 和 data2 参数中使用通配符。\n"); n = 1; } if (strcmp(argv[1], "date") == 0) { printf("显示或设置日期。\n\nDATE [/T | date]\n\n显示当前日期设置和输入新日期的提示,请键入\n不带参数的 DATE。要保留现有日期,请按 Enter。\n\n如果命令扩展被启用,DATE 命令会支持 /T 开关;\n该开关指示命令只输出当前日期,但不提示输入新日期。\n"); n = 1; } if (strcmp(argv[1], "doskey") == 0) { printf("编辑命令行,重新调用 Windows 命令,并创建宏。\n\nDOSKEY [/REINSTALL] [/LISTSIZE=size] [/MACROS[:ALL | :exename]]\n[/HISTORY] [/INSERT | /OVERSTRIKE] [/EXENAME=exename] [/MACROFILE=filename]\n[macroname=[text]]\n\n /REINSTALL 安装新的 Doskey 副本。\n/LISTSIZE=size 设置命令历史记录的缓冲区大小。\n/MACROS 显示所有 Doskey 宏。\n/MACROS:ALL 为具有 Doskey 宏的所有可执行文件显示\n所有 Doskey 宏。\n/MACROS:exename 显示指定可执行文件的所有 Doskey 宏。\n/HISTORY 显示存储在内存中的所有命令。\n/INSERT 指定你键入的新文本插入到旧文本中。\n/OVERSTRIKE 指定新文本覆盖旧文本。\n/EXENAME=exename 指定可执行文件。\n/MACROFILE=filename 指定要安装的宏文件。\nmacroname 指定你创建的宏的名称。\ntext 指定要录制的命令。\n\n上下箭头 重新调用命令;Esc 清除命令行;F7\n显示命令历史记录;Alt+F7 清除\n命令历史记录;F8 搜索命令历史记录;F9 按编号选择命令;Alt+F10 清除宏定义。\n\n以下是 Doskey 宏定义的一些特殊代码:\n$T 命令分隔符。允许一个宏中存在多个命令。\n$1-$9 批处理参数。与批处理程序中的 %1-%9 等同。\n$* 以命令行中命令名称后面的任何内容替换的符号。\n"); n = 1; } if (strcmp(argv[1], "exit") == 0) { printf("退出 CMD.EXE 程序(命令解释器)或当前批处理脚本。\n\nEXIT [/B] [exitCode]\n\n/B 指定要退出当前批处理脚本而不是 CMD.EXE。如果从一个\n 批处理脚本外执行,则会退出 CMD.EXE\n\nexitCode 指定一个数字号码。如果指定了 /B,将 ERRORLEVEL\n设成那个数字。如果退出 CMD.EXE,则用那个数字设置\n过程退出代码。\n"); n = 1; } if (strcmp(argv[1], "fc") == 0) { printf("比较两个文件或两个文件集并显示它们之间\n的不同\n\nFC [/A] [/C] [/L] [/LBn] [/N] [/OFF[LINE]] [/T] [/U] [/W] [/nnnn]\n[drive1:][path1]filename1 [drive2:][path2]filename2\nFC /B [drive1:][path1]filename1 [drive2:][path2]filename2\n\n /A 只显示每个不同处的第一行和最后一行。\n/B 执行二进制比较。\n/C 不分大小写。\n/L 将文件作为 ASCII 文字比较。\n/LBn 将连续不匹配的最大值设置为指定\n的行数。\n/N 在 ASCII 比较上显示行数。\n/OFF[LINE] 不要跳过带有脱机属性集的文件。\n/T 不要将制表符扩充到空格。\n/U 将文件作为 UNICODE 文本文件比较。\n/W 为了比较而压缩空白(制表符和空格)。\n/nnnn 指定不匹配处后必须连续\n匹配的行数。\n[drive1:][path1]filename1\n 指定要比较的第一个文件或第一个文件集。\n[drive2:][path2]filename2\n指定要比较的第二个文件或第二个文件集。\n\n"); n = 1; } if (strcmp(argv[1], "find ") == 0) { printf("在文件中搜索字符串。\n\nFIND [/V] [/C] [/N] [/I] [/OFF[LINE]] \"string\" [[drive:][path]filename[ ...]]\n\n/V 显示所有未包含指定字符串的行。\n/C 仅显示包含字符串的行数。\n/N 显示行号。\n/I 搜索字符串时忽略大小写。\n/OFF[LINE] 不要跳过具有脱机属性集的文件。\n\"string\" 指定要搜索的文本字符串。\n[drive:][path]filename\n指定要搜索的文件。\n\n如果没有指定路径,FIND 将搜索在提示符处键入\n的文本或者由另一命令产生的文本。\n"); n = 1; } if (strcmp(argv[1], "findstr") == 0) { printf("在文件中寻找字符串。\n\nFINDSTR [/B] [/E] [/L] [/R] [/S] [/I] [/X] [/V] [/N] [/M] [/O] [/P] [/F:file]\n[/C:string] [/G:file] [/D:dir list] [/A:color attributes] [/OFF[LINE]]\nstrings [[drive:][path]filename[ ...]]\n\n/B 在一行的开始配对模式。\n/E 在一行的结尾配对模式。\n/L 按字使用搜索字符串。\n/R 将搜索字符串作为一般表达式使用。\n/S 在当前目录和所有子目录中搜索匹配文件。\n/I 指定搜索不分大小写。\n/X 打印完全匹配的行。\n/V 只打印不包含匹配的行。\n/N 在匹配的每行前打印行数。\n/M 如果文件含有匹配项,只打印其文件名。\n/O 在每个匹配行前打印字符偏移量。\n/P 忽略有不可打印字符的文件。\n/OFF[LINE] 不跳过带有脱机属性集的文件。\n/A:attr 指定有十六进位数字的颜色属性。请见 \"color /?\"\n/F:file 从指定文件读文件列表 (/ 代表控制台)。\n/C:string 使用指定字符串作为文字搜索字符串。\n/G:file 从指定的文件获得搜索字符串。 (/ 代表控制台)。\n/D:dir 查找以分号为分隔符的目录列表\nstrings 要查找的文字。\n[drive:][path]filename\n指定要查找的文件。\n\n除非参数有 /C 前缀,请使用空格隔开搜索字符串。\n例如: 'FINDSTR \"hello there\" x.y' 在文件 x.y 中寻找 \"hello\" 或\n\"there\"。'FINDSTR /C:\"hello there\" x.y' 文件 x.y 寻找\n\"hello there\"。\n\n一般表达式的快速参考:\n. 通配符: 任何字符\n* 重复: 以前字符或类出现零或零以上次数\n^ 行位置: 行的开始\n $ 行位置: 行的终点\n[class] 字符类: 任何在字符集中的字符\n[^class] 补字符类: 任何不在字符集中的字符\n[x-y] 范围: 在指定范围内的任何字符\nx Escape: 元字符 x 的文字用法\n<xyz 字位置: 字的开始\nxyz> 字位置: 字的结束\n\n有关 FINDSTR 常见表达法的详细情况,请见联机命令参考。\n"); n = 1; } if (strcmp(argv[1], "help") == 0) { printf("提供 Windows 命令的帮助信息。\nHELP [command]\n\n command - 显示该命令的帮助信息。\n"); n = 1; } if (strcmp(argv[1], "more") == 0) { printf("逐屏显示输出。\n\nMORE [/E [/C] [/P] [/S] [/Tn] [+n]] < [drive:][path]filename\ncommand-name | MORE [/E [/C] [/P] [/S] [/Tn] [+n]]\nMORE /E [/C] [/P] [/S] [/Tn] [+n] [files]\n\n[drive:][path]filename 指定要逐屏显示的文件。\n\ncommand-name 指定要显示其输出的命令。\n\n/E 启用扩展功能\n/C 显示页面前先清除屏幕\n/P 扩展 FormFeed 字符\n/S 将多个空白行缩成一行\n/Tn 将制表符扩展为 n 个空格(默认值为 8)\n\n开关可以出现在 MORE 环境变量中。\n+n 从第 n 行开始显示第一个文件\n\nfiles 要显示的文件列表。使用空格分隔列表中的文件。\n如果已启用扩展功能,则在 -- More -- 提示处 接受下列命令:\nP n 显示下 n 行\nS n 跳过下 n 行\nF 显示下个文件\nQ 退出\n= 显示行号\n? 显示帮助行\n<space> 显示下一页\n<ret> 显示下一行\n"); n = 1; } if (strcmp(argv[1], "time") == 0) { printf("显示或设置系统时间。\n\nTIME [/T | time]\n\n显示当前时间设置和输入新时间的提示,请键入\n不带参数的 TIME。要保留现有时间,请按 Enter。\n\n如果命令扩展被启用,TIME 命令会支持 /T 命令行开关;该命令行开关告诉\n命令只输出当前时间,但不提示输入新时间。\n"); n = 1; } if (n == 0) { printf("帮助工具不支持此命令。请尝试\" %s /?\"。\n", argv[1]); } } } //time命令 bool IsLegal(int hour, int minute, int second)//判断时间是否合法的函数 { if (hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59 && second >= 0 && second <= 59) { return true; } else { return false; } } void Time(int argc, char **argv) { int innum = argc; time_t timep; struct tm *p; int hour; int minute; int second; time_t tt; const char s[2] = ":"; char *token; //getopt(argc, argv, "ab"); if (innum == 1) { time(&timep); p = localtime(&timep);//取得当地时间 printf("当前时间:%02d:%02d:%02d\n", p->tm_hour, p->tm_min, p->tm_sec); while (1) { printf("输入新时间:"); scanf("%d:%d:%d", &hour, &minute, &second); //转化为tm结构 p->tm_hour = hour; p->tm_min = minute; p->tm_sec = second; //转化为time_t结构 tt = mktime(p); char ch; while ((ch = getchar()) != '\n' && ch != EOF) ; if (IsLegal(hour, minute, second))//判断时间是否合法 { if (stime(&tt) < 0)//设置时间 { printf("系统无法接受输入的时间\n"); } else { break; } } else { printf("系统无法接受输入的时间\n"); } } } else if (innum == 2) { if (strcmp(argv[1], "/t") == 0) { time(&timep); p = localtime(&timep); printf("当前时间:%02d:%02d:%02d\n", p->tm_hour, p->tm_min, p->tm_sec); } else if (strcmp(argv[1], "?") == 0) { printf("显示或设置系统时间。\n"); printf("\nTIME [/T| time] \n"); printf("\n显示当前时间设置和输入新时间的提示,请键入不带参数的 TIME。要保留现有时间,请按 Enter。\n"); printf("如果命令扩展被启用,TIME 命令会支持 /T 命令行开关;该命令行开关告诉命令只输出当前时间,但不提示输入新时间。\n"); } else { printf("%s", argv[1]); time(&timep); p = localtime(&timep); //通过':'将输入字符分割 if (token = strtok(argv[1], s)) { hour = atoi(token); if (token = strtok(NULL, s)) { minute = atoi(token); if (token = strtok(NULL, s)) { second = atoi(token); p->tm_hour = hour; p->tm_min = minute; p->tm_sec = second; tt = mktime(p); } else { } } else { } } else { } while (1) { if (IsLegal(hour, minute, second)) { if (stime(&tt) < 0) { printf("系统无法接受输入的时间\n"); } else { break; } } else { printf("系统无法接受输入的时间\n"); } printf("输入新时间:"); scanf("%d:%d:%d", &hour, &minute, &second); p->tm_hour = hour; p->tm_min = minute; p->tm_sec = second; tt = mktime(p); char ch; while ((ch = getchar()) != '\n' && ch != EOF) ; } } } else { printf("系统无法接受输入的时间"); } } //date命令 bool IsLeapYear(int year)//判断日期是否合法的函数 { if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) return true; return false; } bool DIsLegal(int year, int mon, int day) { if (year < 0 || mon <= 0 || mon > 12 || day <= 0 || day > 31) return false; if (1 == mon || 3 == mon || 5 == mon || 7 == mon || 8 == mon || 10 == mon || 12 == mon) { return true; } if (IsLeapYear(year)) { if (2 == mon && (28 == day || 30 == day || 31 == day)) return false; return true; } else { if (2 == mon && (29 == day || 30 == day || 31 == day)) return false; return true; } } void date(int argc, char **argv) { char *wday[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; time_t timep; struct tm *p; int innum = argc; int year; int mon; int day; struct tm settime; struct timeval tv; const char s[2] = "-"; char *token; //getopt(argc, argv, "ab"); if (innum == 1) { time(&timep); p = localtime(&timep); //取得当地时间 printf("% d/% d/ % d % s\n", (1900 + p->tm_year), (1 + p->tm_mon), p->tm_mday, wday[p->tm_wday]); while (1) { printf("输入新日期:<年月日>"); scanf("%d-%d-%d", &year, &mon, &day); //将日期转化为tm结构 settime.tm_mday = day; settime.tm_mon = mon - 1; settime.tm_year = year - 1900; //通过tm结构转化我timeval结构 tv.tv_sec = mktime(&settime); tv.tv_usec = 0; char ch; while ((ch = getchar()) != '\n' && ch != EOF)//清空内存中的字符 ; if (DIsLegal(year, mon, day))//判断是否合法 { if (settimeofday(&tv, (struct timezone *)0) < 0)//设置时间 { printf("系统无法接受输入的日期\n"); } else { break; } } else { printf("系统无法接受输入的日期\n"); } } } else if (innum == 2) { if (strcmp(argv[1], "/t") == 0) { time(&timep); p = localtime(&timep); //取得当地时间 printf("% d/% d/ % d % s\n", (1900 + p->tm_year), (1 + p->tm_mon), p->tm_mday, wday[p->tm_wday]); } else if (strcmp(argv[1], "?") == 0) { printf("显示或设置日期。\n"); printf("\nDATE [/T| date] \n"); printf("\n显示当前日期设置和输入新日期的提示,请键入不带参数的DATE。要保留现有日期,请按Enter。\n"); printf("如果命令扩展被启用,DATE命令会支持/T开关:该开关指示命令只输出当前日期,但不提示输出新日期。\n"); } //将输入的字符通过'-'分割 else { if (token = strtok(argv[1], s)) { year = atoi(token); if (token = strtok(NULL, s)) { mon = atoi(token); if (token = strtok(NULL, s)) { day = atoi(token); settime.tm_mday = day; settime.tm_mon = mon - 1; settime.tm_year = year - 1900; tv.tv_sec = mktime(&settime); tv.tv_usec = 0; } else { } } else { } } else { } while (1) { if (IsLegal(year, mon, day)) { if (settimeofday(&tv, (struct timezone *)0) < 0) { printf("系统无法接受输入的日期\n"); } else { break; } } else { printf("系统无法接受输入的日期\n"); } printf("输入新日期:<年月日>"); scanf("%d-%d-%d", &year, &mon, &day); settime.tm_mday = day; settime.tm_mon = mon - 1; settime.tm_year = year - 1900; tv.tv_sec = mktime(&settime); tv.tv_usec = 0; char ch; while ((ch = getchar()) != '\n' && ch != EOF) ; } } } else { printf("系统无法接受输入的日期"); } } //cls命令 void cls(int argc, char **argv) { if (argc == 1) { fputs("\x1b[2J\x1b[H", stdout);//屏幕控制指令,清除屏幕 } else { if (strcmp(argv[1], "?") == 0) { printf("清除屏幕\n"); printf("\nCLS\n"); printf("\n"); } else { fputs("\x1b[2J\x1b[H", stdout); } } } //exit命令 void Exit() { char *f = "kill -9 "; char str[10]; int d = getppid();//获取当前进程的父进程 sprintf(str, "%d", d);//将d转化为字符串 //字符串的拼接 char *command = (char *)malloc(strlen(f) + strlen(str)); strcpy(command, f); strcat(command, str); system(command);//执行系统命令 } //主函数 int main() { char *input = (char *)malloc(2024); int argc; int i; fputs("\x1b[2J\x1b[H", stdout);//清除屏幕 char buf[80]; getcwd(buf, sizeof(buf));//获取当前路径 printf("Microsoft Windows [版本 10.0.18363.535]\n(c) 2019 Microsoft Corporation。保留所有权利。\n"); printf("\n"); printf("%s>", buf); while (gets(input)) { char **argv = (char **)malloc(2024); char *temp = (char *)malloc(2024); argc = 1; i = 0; //对命令按空格进行分割,命令存入*argv[]中,命令数存入argc中 if (strlen(input) != 0) { char *delim = " "; char *p; p = strtok(input, delim); argv[0] = p; while ((p = strtok(NULL, delim))) { argv[argc] = p; argc++; } } //判断命令 if (0 == strcmp(argv[0], "find")) find(argc, argv); else if (0 == strcmp(argv[0], "findstr")) findstr(argv[1], argv[2], argv[3]); else if (0 == strcmp(argv[0], "comp")) comp(argc, argv); else if (0 == strcmp(argv[0], "fc")) fc(argv); else if (0 == strcmp(argv[0], "date")) date(argc, argv); else if (0 == strcmp(argv[0], "time")) Time(argc, argv); else if (0 == strcmp(argv[0], "more")) more(argc, argv); else if (0 == strcmp(argv[0], "cls")) cls(argc, argv); else if (0 == strcmp(argv[0], "help")) help(argc, argv); else if (0 == strcmp(argv[0], "exit")) Exit(); else { printf("命令行有误"); } //释放内存 free(argv); free(temp); printf("%s>", buf); } }
来源:https://www.cnblogs.com/Qi-Lin/p/12207186.html