昨天朋友跟我说我的 MIT6.S081 lab1 的 xargs 命令的代码无法通过,看到他的测试方法才知道原来是这样测试的,感觉之前写的测的都太简略了。

例如测试 xargs 命令这一关可以这样测试:

$ make GRADEFLAGS=xargs grade

之前的博客在这里:MIT6.S081 Lab: Xv6 and Unix Utilities

检查了一下我之前的代码,发现完全没有处理多行的情况,在测试的时候就会出现问题。

偷看一下测试用的脚本:

mkdir a
echo hello > a/b
mkdir c
echo hello > c/b
echo hello > b
find . b | xargs grep hello

所以对于 find . b ,它的结果会是三行:

./a/b
./c/b
./b

管道之后的 xargs 应该执行三次 grep ,这才是正确的行为:

grep hello ./a/b
grep hello ./c/b
grep hello ./b

但是我使用 read() 处理的时候又发现了新的问题,管道不是行缓冲的,不方便直接读入一行,想要换用 scanf() 但是 xv6 没有提供,所以只能一个字符一个字符手动处理:

#include "kernel/types.h"
#include "kernel/param.h"
#include "user/user.h"

int main(int argc, char *argv[]) {
  char buf[1024];
  char *cmd[MAXARG];
  memset(buf, 0, 1024);
  memset(cmd, 0, sizeof(char *) * MAXARG);
  for (int i = 1; i < argc; ++i) {
    cmd[i-1] = argv[i];
  }
  char ch;
  char *p = buf;
  int blanked = 1;
  int ncmd = argc - 1;
  while (read(0, &ch, 1) > 0) {
    if (ch == ' ' || ch == '\t') {
      *p = '\0';
      blanked = 1;
      p += 1;
    } else if (ch == '\n') {
      if (fork() == 0) {
        exec(cmd[0], cmd);
      } else {
        wait(0);
      }
      memset(buf, 0, sizeof buf);
      p = buf;
      ncmd = argc - 1;
    } else {
      if (blanked) {
        blanked = 0;
        cmd[ncmd] = p;
        ncmd += 1;
      }
      *p = ch;
      p += 1;
    }
  }
  exit(0);
}