不要一开始就看着最难的东西 从小处入手 其实这个实验本来也是这么设计的
比如评分标准里首先是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++)
{
// 每一轮循环都是找到输入的命令中的一个word,比如 echo hi ,就是先找到echo,再找到hi
// 让argv[i]分别指向他们的开头,并且将echo,hi后面的空格设为\0
// 跳过之前的空格
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
这篇博客介绍了如何从简单开始实现MIT-6.s081操作系统实验室中的XV6 shell。作者提供了GitHub链接,首先讲解了如何实现简单的echo功能,然后逐步引入IO重定向和简单管道。对于更复杂的重定向和管道组合,如'grep lion < data.txt | wc > count',作者建议分步思考并使用fork-exec策略。最后,提及会分享更多关于代码的总结。
7360





