简单实现的dos命令

CLS, DATE,TIME,FIND,FINDSTR,COMP,FC,EXIT,HELP,MORE

说明

结构

为了演示,所用到的两个文档1.txt 2.txt

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第1张 为Linux 操作系统建立兼容的 Windows命令接口 随笔 第2张

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

CLS

功能

cls命令的功能是清屏

设计流程

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第3张 其实就是简单地 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 设置前景色

效果

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第4张 为Linux 操作系统建立兼容的 Windows命令接口 随笔 第5张

DATE

功能

date 命令用来查看和修改当前日期 详细操作查看https://jingyan.baidu.com/article/1974b2893e7d62f4b1f774ff.html

设计流程

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第6张 判断是否有输入参数t,?,进行相关处理。 如果没有则打印当前日期,并提示输入更改日期,判断日期是否合法再进行修改 判断年月日是否合法直接用了这位博主的代码 https://blog.csdn.net/freeape/article/details/48682411

效果

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第7张 为Linux 操作系统建立兼容的 Windows命令接口 随笔 第8张

TIME

功能

time 用来查看和修改计算机时间 详细操作查看https://jingyan.baidu.com/article/aa6a2c14ab5ac90d4c19c492.html

设计流程

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第9张 判断是否有输入参数t,?,进行相关处理。 如果没有则打印当前时间,并提示输入更改时间,判断时间是否合法再进行修改

效果

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第10张

FIND

在之后涉及比较的命令的程序都参考了https://zhidao.baidu.com/question/237942227.html下一骑当后的回答

功能

find 命令用于查找文档中的特定字符和数字及行号 详细操作查看https://jingyan.baidu.com/article/86f4a73ecfe08637d65269ef.html

设计流程

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第11张 采用指针遍历文件的匹配算法来寻找字符串,并通过输入参数/C/N/V等控制输出打印的形式

效果

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第12张

FINDSTR

这个命令自己实现的并不完全

功能

findstr是find的升级版 详细操作查看https://jingyan.baidu.com/article/a65957f418641e24e67f9b99.html

设计流程

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第13张 为Linux 操作系统建立兼容的 Windows命令接口 随笔 第14张 命令所需参数有文件路径,文件类型和所搜寻的字符串 该模块包含两个函数 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 文件名

效果

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第15张

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)

设计流程

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第16张 为Linux 操作系统建立兼容的 Windows命令接口 随笔 第17张 为Linux 操作系统建立兼容的 Windows命令接口 随笔 第18张 主要包含三个函数comp,command_comp,file_comp分别对应上图1,2,3 comp主要通过控制流程控制程序走向 command_comp主要对输入的参数进行分析,并返回一个和(包含参数信息,/L对应1,/A对应2,/D对应4,无对应0) file_comp主要通过匹配算法进行字符串的查找,并根据对参数指令的分析返回的值进行打印输出

效果

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第19张

FC

功能

FC是DOS及Windows下的一个比较文件的命令行工具,使用该命令能够将两个类似文件的不同之处进行详细对比。 详细操作查看https://baike.baidu.com/item/FC/10362340?fr=aladdin

设计流程

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第20张 比较两个文件的不同之处,循环读取文件1的一行,与文件2的每一行进行对比,输出不相等的行。再循环读取文件2的一行,与文件1的每一行进行对比,输出不相等的行。

效果

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第21张

EXIT

功能

退出当前命令窗口 详细操作查看https://baike.baidu.com/item/FC/10362340?fr=aladdin

设计流程

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第22张 这里实在不会实现,只好假装实现,因为通过命令窗口调用该程序,所以得到该程序的父进程的id,然后将其kill假装实现了exit

效果

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第23张

HELP

功能

help列出命令的帮助信息 详细操作查看https://jingyan.baidu.com/article/ea24bc39d29264da63b33163.html

设计流程

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第24张 直接输入help命令显示所有help信息,通过help+命令,输出对应命令的信息

效果

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第25张

MORE

功能

more 命令将文本文档逐行进行显示, 也可显示多个文档, 跳行和显示下几行 详细操作查看https://jingyan.baidu.com/article/bad08e1e327c3709c85121fa.html 其实这个程序大部分用了github上的代码,可惜后面找不到出处了

设计流程

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第26张 主要包含more see_more do_more几个函数,这里全部展示在上图。 more允许打印多个文件,当打印满时,可以通过命令q退出命令,命令m打印更多,命令\n打印下一个文件。

效果

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第27张 两个文件时 为Linux 操作系统建立兼容的 Windows命令接口 随笔 第28张 M继续显示,\n显示下一个文件 为Linux 操作系统建立兼容的 Windows命令接口 随笔 第29张 为Linux 操作系统建立兼容的 Windows命令接口 随笔 第30张

主函数

设计流程

为Linux 操作系统建立兼容的 Windows命令接口 随笔 第31张

效果

为了模仿windows前面的提示符,通过getcwd()获取当前目录,并显示 为Linux 操作系统建立兼容的 Windows命令接口 随笔 第32张

其它说明

对于格式化显示输出的问题

保留小数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 为Linux 操作系统建立兼容的 Windows命令接口 随笔 第33张

#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);
    }
}
扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄