实验提供了一个shell的简化版本,主要功能是解析shell命令并且执行
在提供的shell代码中已经编写好了对命令进行解析的部分,我们需要的是利用Unix系统调用对命令进行执行
Executing simple commands
任务: 执行简单的命令,如:
$ ls
要完成简单命令的执行,应该补全runcmd函数中“ case ' ' ”部分的代码
查询exec系统调用的man手册:
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);
p - execlp(), execvp(), execvpe()
These functions duplicate the actions of the shell in searching for an executable file if the specified file‐name does not contain a slash (/) character. The file is sought in the colon-separated list of directory pathnames specified in the PATH environment variable. If this variable isn't defined, the path list defaults to a list that includes the directories returned by confstr(_CS_PATH) (which typically returns the value "/bin:/usr/bin") and possibly also the current working directory; see NOTES for further details.
If the specified filename includes a slash character, then PATH is ignored, and the file at the specified pathname is executed.
综合来看,应该选择exevp()系统调用,接受的参数的包括待执行的文件以及参数列表。其中,*file会在PATH环境变量中搜索,如果含有/,则不在PATH中搜索,而将其视为路径名。这样,无论是类似/bin/ls这样的命令,还是ls这样的命令,都能够成功执行
添加的代码如下:
case ' ':
ecmd = (struct execcmd*)cmd;
if(ecmd->argv[0] == 0)
exit(0);
if(execvp(ecmd->argv[0], ecmd->argv) == -1)
fprintf(stderr, "command not found: %s\n", ecmd->argv[0]);
break;
测试的结果如下:
6.828$ ls
makefile note.md sh sh.c t.sh x.txt
6.828$ /bin/ls
makefile note.md sh sh.c t.sh x.txt
I/O redirection
任务: 实现 < 和 > 指示的I/O重定向功能
这里要使用open系统调用打开重定向指示的文件,查询man手册可知,open接受两个或三个参数:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
pathname是要打开的文件路径,flags是打开文件所要使用的操作,mode是如果新建文件,文件访问权限的初始值,如果没有新建文件,mode参数会被忽略,参数的具体值详见man手册
这里因为可能需要新建文件,所以使用三个参数的版本:
open(rcmd->file, rcmd->mode, S_IRWXU | S_IRWXG | S_IRWXO)
注意(都是本人踩过的坑):
- 在调用open前记得调用close关闭原本的文件描述符rcmd->fd
- 关注mode参数的值,避免新建的文件无法被后面的命令打开读取
添加的完整代码如下:
case '>':
case '<':
rcmd = (struct redircmd*)cmd;
close(rcmd->fd);
if(open(rcmd->file, rcmd->mode, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
{
fprintf(stderr, "fail to open file %s\n", rcmd->file);
exit(EXIT_FAILURE);
}
runcmd(rcmd->cmd);
break;
测试的结果如下:
6.828$ echo operating-system > x.txt
6.828$ cat < x.txt
operating-system
Implement pipes
任务: 实现通过管道执行连续的命令
通过Xv6手册的内容结合pipe的man手册的内容可以对管道的使用有一个大概的了解,这里实际上是通过新建一个进程,由父子进程分别执行管道左右两端的命令,通过管道将一侧的命令的输出传递给另一侧命令的输入;这本质上也是一种重定向,将左侧命令的输出重定向,输出到管道的输入端,将右侧命令的输入重定向,从管道的输出端输入
通过man手册查询实验中提示的pipe, fork, close, dup系统调用之后,就可以成功编写出代码了
推荐使用dup2:
int dup2(int oldfd, int newfd);
功能是关闭newfd指向的文件,并让newfd指向oldfd指向的文件,如果newfd和oldfd指向同一个文件,dup2不作任何操作,如果oldfd不是合法的文件描述符,则dup2不会关闭newfd指向的文件,并且系统调用会失败
添加的代码如下:
case '|':
pcmd = (struct pipecmd*)cmd;
if(pipe(p) == -1)
{
fprintf(stderr, "fail to create a pipe\n");
exit(EXIT_FAILURE);
}
if(fork() == 0)
{
dup2(p[1], STDOUT_FILENO);
runcmd(pcmd->right);
}
else
{
dup2(p[0], STDIN_FILENO);
runcmd(pcmd->left);
}
break;
测试的结果如下:
2440078$ ls | sort | uniq | wc
makefile note.md sh sh.c t.sh x.txt
标签:shell,const,int,char,sh,file,open,MIT,Homework
From: https://www.cnblogs.com/tea-in-the-snow/p/16880042.html