内容回顾
上一节我们探讨了父子进程间通过匿名管道进行通信的原理。管道作为特殊的内存级文件,仅支持单向读写。为避免数据混乱,通常由一个进程写入,另一个进程读取,双方需正确关闭不用的读写端。
Linux 的匿名管道指令
Linux 中常见的 cmd1 | cmd2 语法本质就是匿名管道。Shell 解释器通过 fork 创建子进程,并在 fork 前调用 pipe 创建管道。子进程继承文件描述表后,标准输出被重定向至管道写端,标准输入被重定向至读端,从而实现进程间的数据流传递。
完善 Shell 外壳程序
整体框架
实现支持管道的 Shell 主要包含四个模块:获取用户输入、解析字符串、判断指令类型、执行指令。
1. 获取用户输入的字符串
由于输入可能包含空格和管道符,scanf 会在空格处截断,因此必须使用 fgets 读取整行。
char temp[MAX_SIZE];
if(fgets(temp,sizeof(temp),stdin)==NULL){
perror("fgets");
continue;
}
2. 解析字符串
我们需要统计基本指令个数,并分割指令部分、参数部分以及重定向信息。为此定义了一个 order 类来封装指令属性。
class order{
public:
char* _argv[MAX_SIZE];
int _argc=0;
int _check_redir=0;
char* filename=nullptr;
};
解析流程分为两步:先用 strtok 以 | 分割出所有基本指令存入临时数组,再逐个解析每个指令的参数。这里有一个关键陷阱:不能边分割边解析。因为 strtok 内部维护静态指针,若在 pipe_string 中调用 strtok 后,立即在 get_string 中再次调用 strtok,会覆盖之前的指针位置,导致后续指令丢失。
int pipe_string(std::vector<order>& order_array,char temp[]){
int cmdnum=0;
char* tmp[MAX_SIZE];
* token=(temp,);
(token!=&&cmdnum<MAX_SIZE){
tmp[cmdnum]=token;
cmdnum++;
token=(,);
}
( i=;i<cmdnum;i++){
(order_array[i],tmp[i]);
}
cmdnum;
}


