shell 要求完成的功能有三:
shell要求可以调用可执行程序, 可以用execv函数来实现
头文件:#include
定义函数:int execv (const char * path, char * const argv[]);
函数说明:execv()用来执行参数path 字符串所代表的文件路径, 与execl()不同的地方在于execv()只需两个参数, 第二个参数利用数组指针来传递给执行文件。
返回值:如果执行成功则函数不会返回, 执行失败则直接返回-1, 失败原因存于errno 中。
exec 函数传递的 filename参数按照 PATH 环境变量,在指定的各个目录中寻找可执行文件。
PATH 环境变量包含一张目录表(称为路径前缀),目录之间用冒号(:)分割。例如,
PATH=/bin:/usr/bin:/usr/local/bin:.
为了实现PATH环境变量, 使得shell可以自主根据PATH变量需找可执行程序, 并且在将来可提供方法修改环境变量,如setenv, 达到灵活使用的目的,
我加入了如下内容:
//1.使用全局变量暂代环境变量
char PATH[100]="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:.";
//2.采用strtok方法分割环境变量,并通过循环不断尝试PATH中的路径
char token[100] = "0";
strcpy(token, PATH);
char * path_tem = strtok( token, ":");
while (path_tem != NULL)
{
...
}
//此处需要注意 char* 与 char[] 的区别
此处需要注意open函数close函数以及权限的分配.
open函数
打开文件操作使用系统调用函数open(),该函数的作用是建立一个文件描述符,其他的函数可以通过文件描述符对指定文件进行读取与写入的操作。
头文件:
#include
#include
#include
定义函数:
int open(const char * pathname, int flags);
int open(const char * pathname, int flags, mode_t mode);
返回值:若所有欲核查的权限都通过了检查则返回0 值, 表示成功, 只要有一个权限被禁止则返回-1.
mode参数只有在建立新文件时才会生效, 此外真正建文件时的权限会受到umask 值所影响, 因此该文件权限应该为 (mode-umask)
close函数关闭一个已打开的文件:
#include
int close(int fd);
返回值:成功返回0,出错返回-1并设置errno
参数fd是要关闭的文件描述符.
由open返回的文件描述符一定是该进程尚未使用的最小描述符.由于程序启动时自动打开文件描述符0、1、2,因此第一次调用open打开文件通常会返回描述符3,再调用open就会返回4。
可以利用这一点在标准输入、标准输出或标准错误输出上打开一个新文件,实现重定向的功能。例如,首先调用close关闭文件描述符1,然后调用open打开一个常规文件,则一定会返回文件描述符1,这时候标准输出就不再是终端,而是一个常规文件了,再调用printf就不会打印到屏幕上,而是写到这个文件中了。
The parser already recognizes “>” and “<”
在给出代码中, cmd已被初始化, fd的值, file, 与mode都已分析完成, 只需直接传入 open函数即可
struct cmd*
redircmd(struct cmd *subcmd, char *file, int type)
{
struct redircmd *cmd;
cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = type;
cmd->cmd = subcmd;
cmd->file = file;
cmd->mode = (type == '<') ? O_RDONLY : O_WRONLY|O_CREAT|O_TRUNC;
cmd->fd = (type == '<') ? 0 : 1;
return (struct cmd*)cmd;
}
重定向完成代码如下:
close(rcmd->fd);
if(open(rcmd->file, rcmd->mode, 0777) < 0)
{
fprintf(stderr, "Open :%s failed\n", rcmd->file);
exit(0);
}
runcmd(rcmd->cmd);