卖萌的弱渣

I am stupid, I am hungry.

Mit6.828 Homework: Shell

常用Shell 命令

  1. ls > y 把ls结果输出到文件y中
  2. cat cat命令的用途是连接文件或标准输入并打印。这个命令常用来显示文件内容,或者将几个文件连接起来显示,或者从标准输入读取内容并显示,它常与重定向符号配合使用
    • 一次显示整个文件:cat filename
    • 从键盘创建一个文件:cat > filename 只能创建新文件,不能编辑已有文件
    • 将几个文件合并为一个文件:cat file1 file2 > file
  3. uniq uniq命令常用语报告或者消除文件中的重复内容,一般与sort命令结合使用
  4. wc wc命令的功能为统计指定文件中的字节数、字数、行数, 并将统计结果显示输出
1
2
ls > y
cat < y | sort | uniq | wc > y1

功能:把当前目录ls的结果存到y中,然后读取y的内容,然后将y的内容排序,去掉重复,然后统计字数,行数.并把结果保存到y1


什么是Unix Pipeline

举个例子: ls -1 | grep p | more 只列出来还有字母p的当前目录下的文件

Pipeline 使用”|“来区分多个命令,从左到右,前一个命令的结果是后一个命令的输入


sh.c 数据结构

1
2
3
4
struct cmd{...}
struct execcmd{...}  // 普通指令
struct redicmd{...}  // 有重定向的指令
struct pipecmd{...}  // pipe指令

sh.c 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
void runcmd(struct cmd *cmd)
int getcmd(char* buffer, int nbuf) // 检测输入是否是标准输入流stdin,然后把用户输入存到buffer里
struct cmd* execcmd() // 为一个cmd数据结构分配内存
struct cmd* redircmd(struct cmd *subcmd, char *file, int type)
struct cmd* pipecmd(struct cmd *left, struct cmd *right)
int gettoken(chasr **ps, char *es, char **q, char **eq) // 把地址ps到es的字符串中的变量找到,并存到q到eq的地址去
int peek(char **ps, char *es, char *toks) //判断从地址ps到es的字符串是否含有toks里面的字符
char *mkcopy(char *s, char *es) // s指向需要拷贝的字符串头,es指向需要拷贝的字符串结尾. 这个函数拷贝从s到es的字符串,然后返回拷贝的地址。
struct cmd* parsecmd(char *s) // 解析命令把buffer里的命令包装成可执行的数据结构struct cmd
struct cmd* parseline(char **ps, char *es)
struct cmd* parsepipe(char **ps, char *es)
struct cmd* parsedirs(struct cmd *cmd, char **ps, char *es)
struct cmd* parseexec(char **ps, char *es)

用到的C和操作系统函数

  • strchr 函数原型:extern char strchr(char str,char character) 参数说明:str为一个字符串的指针,character为一个待查找字符。 所在库名:#include <string.h> 函数功能:从字符串str中寻找字符character第一次出现的位置。

  • execv 函数原型:execv(char cmd, char argv[]); 函数说明:execv 本质上是执行命令 cmd, *argv[] 是包括argv[0]的参数 举例: c execv(argv[0], argv);

  • open 函数原型:int open(const char *pathname, int flags, mode_t mode); 函数说明:

    • creates a new open file description, an entry in the system-wide table of open files. The new open file description is initially not shared with any other process, but sharing may arise via fork
    • parameter flags must include one of the following access modes: O_RDONLY, O_WRONLY, or O_RDWR.
    • mode: 创建文件时设定的其他用户权限: S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; 举例说明
    • 打开一个新文件用来写入
      #include <fcntl.h>
      int fd;
      mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
      char *filename = "/tmp/file";
      fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, mode);
    

    当打开的文件不存在时 flag 必须有O_CREATE, 打开已存在文件则不需要.

  • dup 函数原型: int dup(int oldfd); 函数说明: create a copy of the file descriptor oldfd

    dup uses the lowest-numbered unused descriptor for the new descriptor.

关键部分代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
void runcmd(struct cmd *cmd)
{
  int p[2], r;
  struct execcmd *ecmd;
  struct pipecmd *pcmd;
  struct redircmd *rcmd;
  char cmdPath[10] = "/bin/";


  if(cmd == 0)
    exit(0);

  switch(cmd->type){
  default:
      fprintf(stderr, "unknown runcmd\n");
      exit(-1);

  case ' ':                       // 普通命令
      ecmd = (struct execcmd*)cmd;
      if(ecmd->argv[0] == 0)
        exit(0);

      // Your code here ...
      if(execv(ecmd->argv[0],ecmd->argv)==-1){
      // 到/bin目录中找命令
          char cmdPath[10] = "/bin/";
          strcat(cmdPath,ecmd->argv[0]);
          if(execv(cmdPath,ecmd->argv)==-1){   // test /bin/cmd
              char cmdPath2[15] = "/usr/bin/";
              strcat(cmdPath2,ecmd->argv[0]);   // test /usr/bin/cmd (e.g.sort)
              if(execv(cmdPath2,ecmd->argv)==-1){
                  fprintf(stderr, "Command %s not found\n",ecmd->argv[0]);
              exit(0);
            }

        }

      }
      break;

    case '>':                       // 重定向命令
    case '<':
        rcmd = (struct redircmd*)cmd;
        //fprintf(stderr, "redir not implemented\n");
        // Your code here ...
        close(rcmd->fd);     // 重定向命令已经默认的fd指向了0或1,所以这里是关闭stdin 或者 stdout
        if(open(rcmd->file,rcmd->mode,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)<0){
            fprintf(stderr,"Cannot open file\n");
            exit(0);
        }
        runcmd(rcmd->cmd);
        break;

    case '|':                      //  管道线命令
        pcmd = (struct pipecmd*)cmd;
        // fprintf(stderr, "pipe not implemented\n");
        // Your code here ...
        if(pipe(p)<0){
            fprintf(stderr,"Create pipe failes\n");
            exit(0);
        }
        // 子进程把pipe的right end 和 标准输出连起来
        if(fork1()==0){
            close(1);
            dup(p[1]); // 标准输出被赋予fd:p[1]
            close(p[0]);
            close(p[1]);  // 这样fd表里只剩下标准输入fd:0 和输出fd:p[1]
            runcmd(pcmd->left);
        }
        if(fork1()==0){
            close(0);
            dup(p[0]); // 标准输入被赋予fd:p[0]
            close(p[0]);
            close(p[1]); // 这样fd表里只剩下标准输入fd:fd[0] 和 输出fd:p[0]
            runcmd(pcmd->right);
        }
        close(p[0]);
        close(p[1]);
        wait(); // 父进程等待子进程结束。
        wait();

        break;
      }
      exit(0);
  }