实验说明:https://pdos.csail.mit.edu/6.828/2019/labs/sh.html
离上次做完实验一快有二十多天了 期间不是不想做这个实验 主要是两个困难:
1.不会使用gdb
2. 要从头实现一个shell,并且要处理
grep lion < data.txt | wc > count
这种看起来很复杂的命令,我被吓住了,因为传说中的AST我不会,而有些博客写的用字符串处理的方式因为比较个性化我又看不懂,所以觉得很难,做起来很难受,不过总的来说,这二十多天里,大部分时间我不是在解决问题,而是在逃避问题
总算这一周有了突破,除了实验上,更多是心理上和认知上:心理上主要就是增强自信了(指实现突破后
认知上的突破在于:不要一开始就看着最难的东西 从小处入手 其实这个实验本来也是这么设计的
比如评分标准里首先是simple echo,单纯实现这个的话还是挺简单的,只需要设置一下exec的参数即可(把命令行的指令split一下,当然c语言没有split,所以write from scratch确实有些难度)
然后比较简单的是simple io redirection,simple pipe,之所以是simple,指的是这种指令:
echo hello > file
wc < file
cat file | wc
很直接 只含有三者其一 所以判断一下 就可以写出相应的代码
比较让人觉得难的是both redirection,pipe and redirect
就是本文一开始提到的那种 不熟的话看起来会觉得很难处理 不过也可以start small:即给出一个具体的例子 思考应该怎么处理
比如:
grep lion < data.txt | wc > count
我们首先根据 | 的位置 ,把命令行的输入分开,因为他的两边都是指令,需要fork-exec
然后利用一点类似递归的技巧,就可以完成,至少可以通过测试了~
// 先贴代码吧 改天再总结
#include "kernel/types.h"
#include "user/user.h"
#include "kernel/fcntl.h"
void execPipe(char*argv[],int argc);
//*****************START from sh.c *******************
#define MAXARGS 10
#define MAXWORD 30
#define MAXLINE 100
int getcmd(char *buf, int nbuf)
{
fprintf(2, "@ ");
memset(buf, 0, nbuf);
gets(buf, nbuf);
if (buf[0] == 0) // EOF
return -1;
return 0;
}
char whitespace[] = " \t\r\n\v";
char args[MAXARGS][MAXWORD];
//*****************END from sh.c ******************
void setargs(char *cmd, char* argv[],int* argc)
{
// 让argv的每一个元素都指向args的每一行
for(int i=0;i<MAXARGS;i++){
argv[i]=&args[i][0];
}
int i = 0; // 表示第i个word
// int k = 0; // 表示word中第k个char
int j = 0;
for (; cmd[j] != '\n' && cmd[j] != '\0'; j++)
{
// 跳过之前的空格
while (strchr(whitespace,cmd[j])){
j++;
}
argv[i++]=cmd+j;
// 只要不是空格,就j++,找到下一个空格
while (strchr(whitespace,cmd[j])==0){
j++;
}
cmd[j]='\0';
}
argv[i]=0;
*argc=i;
}
// void runcmd(char *cmd)
void runcmd(char*argv[],int argc)
{
for(int i=1;i<argc;i++){
if(!strcmp(argv[i],"|")){
// 如果遇到 | 即pipe,至少说明后面还有一个命令要执行
execPipe(argv,argc);
}
}
// 此时是仅处理一个命令:现在判断argv[1]开始,后面有没有>
for(int i=1;i<argc;i++){
// 如果遇到 > ,说明需要执行输出重定向,首先需要关闭stdout
if(!strcmp(argv[i],">")){
close(1);
// 此时需要把输出重定向到后面给出的文件名对应的文件里
// 当然如果>是最后一个,那就会error,不过暂时先不考虑
open(argv[i+1],O_CREATE|O_WRONLY);
argv[i]=0;
// break;
}
if(!strcmp(argv[i],"<")){
// 如果遇到< ,需要执行输入重定向,关闭stdin
close(0);
open(argv[i+1],O_RDONLY);
argv[i]=0;
// break;
}
}
exec(argv[0], argv);
}
void execPipe(char*argv[],int argc){
int i=0;
// 首先找到命令中的"|",然后把他换成'\0'
for(;i<argc;i++){
if(!strcmp(argv[i],"|")){
argv[i]=0;
break;
}
}
// 先考虑最简单的情况:cat file | wc
int fd[2];
pipe(fd);
if(fork()==0){
// 子进程 执行左边的命令 把自己的标准输出关闭
close(1);
dup(fd[1]);
close(fd[0]);
close(fd[1]);
// exec(argv[0],argv);
runcmd(argv,i);
}else{
// 父进程 执行右边的命令 把自己的标准输入关闭
close(0);
dup(fd[0]);
close(fd[0]);
close(fd[1]);
// exec(argv[i+1],argv+i+1);
runcmd(argv+i+1,argc-i-1);
}
}
int main()
{
char buf[MAXLINE];
// Read and run input commands.
while (getcmd(buf, sizeof(buf)) >= 0)
{
if (fork() == 0)
{
char* argv[MAXARGS];
int argc=-1;
setargs(buf, argv,&argc);
runcmd(argv,argc);
}
wait(0);
}
exit(0);
}
然后评分:
red@red-vm-ubuntu:~/6s081/xv6-riscv-fall19$ ./grade-lab-sh
make: 'kernel/kernel' is up to date.
running nsh tests: (4.6s)
simple echo: OK
simple grep: OK
two commands: OK
output redirection: OK
input redirection: OK
both redirections: OK
simple pipe: OK
pipe and redirects: OK
lots of commands: OK
Score: 100/100