重定向
即通过重定向操作符,使得输入输出位置发生更改的功能,称为重定向;
实现之前
为了简化实现过程,我们采用之前所完成的自制shell
来实现重定向;自制shell在此
下面分别介绍一下三个常见的重定向符号:
输入重定向(<
)
<
:用于将文件的内容作为命令的标准输入。
command < filename
- 功能:将
filename
的内容作为command
的标准输入来处理。
输出重定向(>
)
>
:用于将命令的标准输出重定向到文件中,会覆盖目标文件中的原有内容。
command > filename
- 功能:将
command
的标准输出写入filename
中;如果文件已存在,则会覆盖原有内容;如果文件不存在,则创建该文件。
追加重定向(>>
)
>>
:用于将命令的标准输出重定向到文件中,但它会追加输出到文件的末尾,不会覆盖原有内容。
command >> filename
- 功能:将
command
的标准输出追加到filename
的末尾;如果文件不存在,则会创建该文件。
具体实现
确定处理状态
首先考虑命令的变化:当进行重定向时,命令行会发生变化,比如
ls > test.txt
可以看到,这次我们不仅需要对指令进行识别,还需要对重定向的符号进行判断,以确定使用什么重定向方式:
// 简单的框架,演示如何读取重定向符号
void checkCommand(char* command){
char* start = command;
char* end = command + strlen(command);
// 构建一个循环,用于找出重定向符号
while(start != end){
// 可能是输出或追加重定向
if(*start == '>'){
start++;
if(*start == '>'){
}
}
// 输入重定向
else if(*start == '<'){
}
}
}
接下来我们采用标志位的方式,对所需要进行的重定向种类进行标记:
// 不做处理
#define NONE_REDIR 0
// 输入重定向
#define INPUT_REDIR 1
// 输出重定向
#define OUTPUT_REDIR 2
// 追加重定向
#define APPEND_REDIR 3
最后,我们根据标记进行集中处理; 下面,我们对上述函数进行进一步完善,以提取出可供重定向的信息:
void checkCommand(char* command){
redir_file = NULL;
redir_type = NONE_REDIR;
assert(command);
char* start = command;
char* end = command + strlen(command);
while(start != end){
umask(0);
if(*start == '<'){ // 输入重定向
*start = '\0';
start++;
skip_space(start);
redir_file = start; // 设置读取位置
redir_type = INPUT_REDIR; // 设置处理状态
break;
}
else if(*start == '>'){ // 输出重定向 or 追加重定向 ?
*start = '\0';
start++;
if(*start == '>'){ // 追加重定向
redir_type = APPEND_REDIR;
++start;
skip_space(start);
redir_file = start;
break;
}
skip_space(start);
redir_file = start;
redir_type = OUTPUT_REDIR;
break;
}
else{
++start;
}
}
}
这里添加了一个skip_space
函数,其目的是为了防止用户输出时包含的空格影响读取工作,其具体实现采用了宏函数的编写原则,只是为了减小函数调用的成本,普通函数也可实现:
#define skip_space(str) do{\
while(isspace(*str)) ++str;\
}while(0) // 需要注意这里的宏函数的编写原则
执行处理
注意,这里的处理同shell
程序一样,也是在子进程下启动的(这也是为什么采用自制的shell
程序处理的原因,因为自制shell
源代码简单且可控,方便我们纠错);
下面是代码实现:
if(id == 0){ // 子进程中启动程序
int fd = 0;
switch(redir_type){
case INPUT_REDIR: // 判断处理状态,执行相应处理
{
fd = open(redir_file, O_RDONLY); // 只读
if(fd < 0){
perror("wrong open");
exit(EXIT_FAILURE);
}
dup2(fd, STDIN_FILENO); // 输入的文件标识符
break;
}
case OUTPUT_REDIR:
{
fd = open(redir_file, O_WRONLY | O_CREAT); // 只写 + 不存在创建
if(fd < 0){
perror("wrong open");
exit(EXIT_FAILURE);
}
dup2(fd, STDOUT_FILENO); // 输出的文件标识符
break;
}
case APPEND_REDIR:
{
redir_type = APPEND_REDIR;
fd = open(redir_file, O_WRONLY | O_CREAT); // 只写 + 不存在创建
if(fd < 0){
perror("wrong open");
exit(EXIT_FAILURE);
}
dup2(fd, STDOUT_FILENO);
break;
}
case NONE_REDIR:
{
break;
}
default:
{
perror("default condition");
// exit(EXIT_FAILURE);
break;
}
}
close(fd);
execvp(myargv[0], myargv);
exit(0);
}
waitpid(id, NULL, 0);
综上,我们便完成了在自制shell
程序中添加重定向功能的任务,具体的完整代码可见我的GitHub
主页:
X_Nefertar,shell
代码见Linux_Learning