如果看过上一篇的话,还会记得在本Shell中main函数内第一条实质性的指令是
getTaskList(task_queue);
在本节,就会讲解这个获取任务列表,并存储到任务队列中的函数,到底在干什么。
我先把整个函数摆在下面,然后逐一讲解(先不要看下面这一大段代码)
void getTaskList(queue<string> & task_queue)
{
while(!task_queue.empty()) task_queue.pop();
string command_line;
getline(cin, command_line);
if(cin.eof())
{
exit(0);
}
if(command_line.empty())
return;
if(command_line.back() == '&')
{
getIfDetach() = true;
command_line.erase(command_line.end() - 1);
if(command_line.empty())
return;
}
else
{
getIfDetach() = false;
}
auto base_iter = command_line.begin();
for(auto off_iter = command_line.begin(); off_iter != command_line.end(); ++ off_iter )
{
if(*off_iter == '|')
{
if(off_iter != command_line.begin() && *(off_iter - 1) == '\\')
{
off_iter = command_line.erase(off_iter - 1);
continue;
}
task_queue.push(string(base_iter, off_iter));
base_iter = off_iter + 1;
}
}
task_queue.push(string(base_iter, command_line.end()));
return;
}
首先函数原型为:
void getTaskList(queue<string> & task_queue);
传入一个std::queue<string>的引用,并把读到的指令按先后顺序存入队列中,之所以使用队列的结构是为了方便使用时先进先出的逻辑。
while(!task_queue.empty()) task_queue.pop();
上述代码块用于检测传入的指令队列是否为空,如果不为空,则将其pop至空,主要是queue没有vector等结构的clear函数,比较麻烦。
string command_line;
getline(cin, command_line);
if(cin.eof())
{
exit(0);
}
一般Shell中命令都是一行一行输入的,因此读入一行命令,存入command_line是本Shell的获取命令的方式。同时,如果输入ctrl+d也就是文件结束符的话,则退出Shell(模仿bash)。
if(command_line.empty())
return;
if(command_line.back() == '&')
{
getIfDetach() = true;
command_line.erase(command_line.end() - 1);
if(command_line.empty())
return;
}
else
{
getIfDetach() = false;
}
如果读入的命令行为空的话,直接返回。
如果读入的命令行的最后一个字符为’&’的话,则说明本次输入的命令行为后台执行,将getIfDetach()函数内的static变量Detach置为true,并调用string类的erase函数删除最后的&字符。同时,如果删除&后命令行为空的话,说明用户仅输入了一个&字符,那么也直接返回。
如果最后一个字符不为’&’,则将Detach变量置为false。
auto base_iter = command_line.begin();
for(auto off_iter = command_line.begin(); off_iter != command_line.end(); ++ off_iter )
{
if(*off_iter == '|')
{
if(off_iter != command_line.begin() && *(off_iter - 1) == '\\')
{
off_iter = command_line.erase(off_iter - 1);
continue;
}
task_queue.push(string(base_iter, off_iter));
base_iter = off_iter + 1;
}
}
这一段可能比较难以理解,实际上我们在做的事情是,当用户使用管道调用多个进程时,我们需要把这几个进程从命令行中分开,然后分别存入task_queue中。首先让base_iter为command_line的头迭代器。托C++11的福,一个auto关键字真是解放劳动力。
接下来让off_iter从命令行头至命令行尾遍历,每当发现一个|字符(也就是管道的调用标识),则将base_iter至off_iter之间的字符串存入task_queue中,同时更新base_iter为off_iter的下一个。
如下图所示,给出了以`ls | grep main | more为例的处理流程:
注:task_queue中存储的命令是以进程为最小单位,也就是命令行参数和进程是作为同一个任务存到该队列中,并没有分开,如本例中的grep main
task_queue.push(string(base_iter, command_line.end()));
最后将最后一段命令存入任务列表,即完成该函数全部工作。也就是上例中存入more的步骤。

本文详细介绍了如何在Linux Shell中实现获取并处理任务列表的功能。通过读取用户输入的命令行,按行存储到任务队列中,遵循先进先出的原则。遇到后台执行标志'&'时调整执行状态,利用管道符号'|'拆分命令,将每个进程作为独立任务存入队列。文章以实例解析了处理流程,帮助理解Shell如何管理任务。
456

被折叠的 条评论
为什么被折叠?



