文件读写
myecho
1.实现功能
myecho.c的功能与系统echo程序相同,接受命令行参数,并将参数打印出来。
2.实现思路
main函数有两个参数:argc和argv,其中argc记录了main 函数的命令行参数总个数,包括可执行程序名;argv[] 是一个字符串数组,每个元素指向一个参数,在命令行输入的字符串会被自动分割为字符串数组,其长度为argc,argv[0]=可执行程序名,argv[1…argc-1]=可执行程序参数,argv[argc] = NULL。
利用main函数的两个参数可以实现echo功能:首先根据argc的值判断是否有需要打印的字符串,如果argc==1,说明只有可执行程序名,没有参数,直接输出 Character not received!
并返回;
/* user haven't input any char */
if(argc == 1){
printf("Character not received!\n");
return 0;
}
如果不为0,输出 argv 字符串数组中的值即可。需要注意的是:argv[0]中存储的是可执行程序名称,不应该被输出,所以下标应从 1 开始:
/* print char from argv */
for(int i = 1 ;i < argc ; i++)
printf("%s ",argv[i]);
printf("\n");
mycat
1.实现功能
mycat.c的功能与系统cat程序相同,mycat将指定的文件内容输出到屏幕,可以处理无参数和多参数的情况,要求使用系统调用open/read/write/close实现。
2.实现思路
mycat在输出文件内容时主要通过函数调用read/write
实现,read函数返回值为rd,如果读取失败,返回-1,如果读取成功,返回实际读取的字节个数;返回0则代表读取到了文件末尾,可以用if语句作为是否读完的条件判断,再将读到的rd个字节用write写出到标准输出 STDOUT_FILENO
。
mycat在没有参数时,也就是argc==1,只有一个程序名,那程序会将用户在屏幕输入的内容原封不动打印出来,此时read的文件描述符参数就是STDIN_FILENO
,表示从屏幕输入。由于输入的内容大小不确定,因此需要开辟一个不是很大的缓冲区来接收字符,通过循环来不断读,这样可以避免输入过大或过小造成的缓冲区溢出或浪费:
if(argc == 1)
{
while(1)
{
int rd;
char buf[10];
if(rd = read(STDIN_FILENO,buf,MAX) > 0)
write(STDOUT_FILENO,buf,rd);
else
break;
}
}
mycat有多个参数时,会将这些文件依次打开并输出到标准输出,因此当 argc>1 时,对 argv 中的文件逐个打开,调用read 读取打开的文件,参数为fd,然后用 write 函数将当前文件的字符写到标准输出中去。同样,由于输入的内容大小不确定,因此需要开辟一个不是很大的缓冲区来接收字符,通过循环来不断读,这样可以避免输入过大或过小造成的缓冲区溢出或浪费:
/* show multiple files */
for(int i = 1;i < argc;i++){
int fd = open(argv[i],O_RDONLY);
if(fd < 0)
panic("open file fail!");
while(1)
{
char buf[10];
memset(buf,0,MAX);
int rd;
if((rd = read(fd,buf,MAX)) > 0)
/* write to screen */
write(STDOUT_FILENO,buf,rd);
else
break;
}
close(fd);
}
mycp
1.实现功能
mycp.c的功能与系统cp程序相同, 将源文件复制到目标文件,要求使用系统调用open/read/write/close实现
2.实现思路
对于mycp,和之前两个程序不同,mpcp需要有可执行程序名argv[0],源文件argv[1],目的文件argv[2],当它的参数小于3个的时候一定是参数错误,因此要先判断 if(argc < 3)
if(argc != 3)
panic("Parameter error!");
对于目的文件,可能不存在,这时应该创建一个新的文件,因此open函数用 O_CREAT,mode
打开,赋予权限为0777,若两次执行程序都移动到同一文件中,需要覆盖操作,因此open函数还需 O_TRUNC
参数。当文件都打开/创建成功时,调用 read 函数对源文件逐个读取,调用 write 函数写入目标文件中。这里write的文件描述符不再是标准输出STDOUT,而是目标文件的fd2:
int fd1 = open(argv[1], O_RDONLY);
mode_t mode=0777;
int fd2 = open(argv[2], O_RDWR | O_TRUNC | O_CREAT,mode);
if(fd1 < 0 || fd2 < 0)
panic("Open file 1 failed!");
char buf[MAX];
memset(buf,0,MAX);
int rd;
while(1)
{
rd = read(fd1,buf,MAX);
if(rd > 0)
write(fd2,buf,rd);
else
break;
}
close(fd1);
close(fd2);
多进程
mysys
1.实现功能
mysys的功能与系统函数system相同,要求用进程管理相关系统调用自己实现一遍,使用fork/exec/wait系统调用。
3.实现思路
mysys的实现主要基于以下三个函数:execvp、strtok、fork
程序main函数读入读入一个字符串 command,传给mysys模块,在mysys模块中,先检查命令长度是否为0,如果没有要执行的命令,则直接返回退出;定义一个myargc表示这个命令的参数个数,定义一个myargv表示参数列表,调用change模块进行字符串处理,返回值为myargc参数个数:
int t=strlen(command);
/* if no command, continue to exec the next one */
if(t == 0)
return;
int myargc;
char *myargv[MAXARG];
myargc = change(command,myargc,myargv);
myargv[myargc]=NULL;
change模块:strtok不可以直接对字符串常量进行分割,要用strncpy得到一个char型数组buf, 对这个buf数组进行处理即可。用空格作为分隔符,首次调用时,第一个参数指向要分解的字符串,之后再次调用要把第一个参数设成NULL,将分割后的子字符串存入 myargv字符数组中,每次myargc++:
int change(char *command,int myargc,char *myargv[])
{
char buf[MAX];
memset(buf,0,MAX);
strncpy(buf,command,strlen(command));
char *delim=" ";
myargc=0;
myargv[myargc]=strtok(buf,delim);
while(myargv[++myargc]=strtok(NULL,delim));
return myargc;
}
分割字符串成功后就开始创建一个子进程,调用子进程执行模块chile,让子进程执行execvp,父进程等待子进程结束再退出:
pid_t pid;
pid = fork();
if(pid == 0)
child(myargc,myargv);
else
wait(NULL);
子进程模块调用execvp,第一个参数为可执行程序名,存在了myargv[0]中,之后的参数列表通过myargv传入,装入可执行程序,执行命令:
void child(int myargc,char *myargv[])
{
int status = execvp(myargv[0],myargv);
if(status < 0)
Perror("Arguments fault!");
exit(1);
}
sh3
1.实现功能
实现管道和文件重定向。
2.实现构图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RfaqS1mh-1593603294451)(C:\Users\lenovo\Desktop\2.PNG)]
3.实现思路
main函数中调用mysys模块:
int main()
{
while (1) mysys();
return 0;
}
mysys模块实现的是内置命令的处理,其他的命令交给parse_command模块。
首先通过fgets读取字符串,并去掉最后一个换行符变为\0:
char command[ARG_MAX + 1], argv[ARG_MAX + 1];
memset(command, 0, ARG_MAX + 1);
printf("> ");
fgets(command, ARG_MAX, stdin);
command[strlen(command) - 1] = '\0';
先把第一个空格之前的子串分离出来分类讨论
如果是内置命令,就不用fork一个子进程,而是直接执行,exit直接调用exit(0)退出,cd调用chdir(path)切换路径;
如果不是内置命令,就交给parse_command来分析命令:
char *p;
p = command;
/* first command */
char temp[10];
memset(temp,0,10);
int i=0,j=0;
while((*p!=' ')&& (*p))
{
temp[i++]=command[j++];
++p;
}
if(strcmp(temp,"exit"