为Linux 操作系统建立兼容的 Windows命令接口
Posted qi-lin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为Linux 操作系统建立兼容的 Windows命令接口相关的知识,希望对你有一定的参考价值。
简单实现的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[2Jx1b[H", stdout);其中的不明所以的字符串是VT100的控制码,部分定义如下 "x1b[2J"清除整个屏幕,行属性变成单宽单高,光标位置不变 "x1b[H"光标移动 33[0m 关闭所有属性 33[1m 设置为高亮 33[4m 下划线 33[5m 闪烁 33[7m 反显 33[8m 消隐 33[nA 光标上移 n 行 33[nB 光标下移 n 行 33[nC 光标右移 n 行 33[nD 光标左移 n 行 33[y;xH 设置光标位置 33[2J 清屏 33[K 清除从光标到行尾的内容 33[s 保存光标位置 33[u 恢复光标位置 33[?25l 隐藏光标 33[?25h 显示光标 33[30m – 33[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打印更多,命令 打印下一个文件。
效果
两个文件时 M继续显示, 显示下一个文件
主函数
设计流程
效果
为了模仿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的分割字符时则会将该字符改为 字符。在第一次调用时,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("
");
}
}
else if (pa == 3)
{
if (flag == 1)
{
printf("%d ", linec);
printf("%s", start);
}
}
linec++;
flag = 0;
}
if (pa == 2 && linen != 0)
{
printf("%d
", 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 == ‘
‘)
{
line++;
}
while (char1 != EOF)
{
if (char1 != char2)
{
if (para == 0)
{
printf("在offset %d比较错误
", offset);
printf("file1=%x
", char1);
printf("file2=%x
", char2);
}
else if (para == 1)
{
printf("在line %d比较错误
", line);
printf("file1=%x
", char1);
printf("file2=%x
", char2);
}
else if (para == 2)
{
printf("在offset %d比较错误
", offset);
printf("file1=%c
", char1);
printf("file2=%c
", char2);
}
else if (para == 4)
{
printf("在offset %d比较错误
", offset);
printf("file1=%d
", (int)char1);
printf("file2=%d
", (int)char2);
}
else if (para == 3)
{
printf("在line %d比较错误
", line);
printf("file1=%c
", char1);
printf("file2=%c
", char2);
}
else
{
printf("在line %d比较错误
", line);
printf("file1=%d
", char1);
printf("file2=%d
", char2);
}
}
char1 = fgetc(f1);
char2 = fgetc(f2);
offset++;
if (char1 == ‘
‘)
{
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
", argv[1], argv[2]);
printf("****%s
", 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
", 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("