#include<iostream>
#include<cstring>
#include<cstdlib>
#include<unistd.h>
#include<sys/wait.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
using namespace std;
const int shellnum =32;
const char * file_all = {"shell-all.txt"};
const char * file_half = {"shell-half.txt"};
//把输入转换为exec函数组可用的参数
void cmd2argv(char **, char *);
//没有 | 的情况
void cmd_no_grep(char **, char *);
//需要管道的情况
void cmd_with_grep(char **, char *, int );
//用来完成管道
bool fileReverse();
//输出到屏幕
void file2Srcn();
int main()
{
char cmd_line[1024];
char * arg[shellnum];
char *cmd_grep = NULL;
//int out_save = dup(STDOUT_FILENO);
//int in_save = dup(STDIN_FILENO);
int time=0;
memset(cmd_line, 0, sizeof(cmd_line));
cout<<">>";
while(fgets(cmd_line, sizeof(cmd_line), stdin))
{
/*
如果有管道的话,相当于是把A的结果作为B的输入
这里的file_all就是用来存放A的结果
所以每次输入命令,需要清空这个文件
*/
int fd = open(file_all, O_TRUNC | O_CREAT, 0644);
close(fd);
cmd_line[strlen(cmd_line)-1] = '\0';
//判断是否有管道
if(strchr(cmd_line, '|'))
{
cmd_grep = strtok(cmd_line, "|");
//cout<<"cmd:"<<cmd_grep<<endl;
while(cmd_grep)
{
cmd_with_grep(arg, cmd_grep,time++);
cmd_grep = strtok(NULL, "|");
}
int fd = open(file_all, O_RDONLY, 0644);
file2Srcn();
close(fd);
}
else
{
cmd_no_grep(arg, cmd_line);
}
cout<<"\n>>";
}
}
void cmd2argv(char **argv, char *cmd)
{
int cnt=0,bg=0,end=0;
while(cmd[bg] != '\0')
{
//去除命令前面的空格
while(cmd[bg] == ' ')
bg++;
end = bg;
//确定命令的结尾
while(cmd[end] != ' ' && cmd[end] != '\0')
end++;
/*
下面这个判断时因为下面这种情况我这段代码会有bug
ls -l (l后面还有两个空格)
这是程序会成功拆开ls和-l 但是此时由于-l后面不是\0
所以while循环还会继续,但是这种情况最后 bg == end
所以用下面这个判断进行避免
*/
if(bg != end)
{
argv[cnt] = new char[end-bg+1];
strncpy(argv[cnt], cmd+bg, end-bg);
//cout<<":"<<argv[cnt]<<":"<<endl;
cnt++;
bg = end+1;
end++;
}
}
argv[cnt] =NULL;
}
void cmd_no_grep(char ** arg, char *cmd_line)
{
cmd2argv(arg, cmd_line);
pid_t pid = fork();
if(0 == pid)
{
execvp(arg[0], arg);
}
else
{
wait(NULL);
}
}
void cmd_with_grep(char ** arg, char *cmd_line, int time)
{
int fd = open(file_all,O_WRONLY | O_CREAT ,0644);
int out_save = dup(STDOUT_FILENO);
int in_save = dup(STDIN_FILENO);
/*
A|B 对于A,它的输入是标准输入,输出的话重定向到文件shell-half.txt
*/
if(time == 0)
{
cmd2argv(arg, cmd_line);
dup2(fd, STDOUT_FILENO);
pid_t pid = fork();
if(0 == pid)
{
execvp(arg[0], arg);
}
else
{
sleep(0.1);
wait(NULL);
close(fd);
}
}
else
{
/*
A|B 对于B来说,输入输出都得重定向
输入 file_all.txt
输出 file_half.txt
所以我们再最下面调用fileReverse()函数把half.txt拷贝到all.txt
(这里这么做是因为,我们在使用exec函数的时候要保证all.txt是我们想要的输入)
*/
int fd_in = open(file_all,O_RDWR ,0644);
int fd_half = open(file_half,O_RDWR | O_CREAT | O_TRUNC, 0644);
cmd2argv(arg, cmd_line);
dup2(fd_in, STDIN_FILENO);
dup2(fd_half, STDOUT_FILENO);
pid_t pid = fork();
if(0 == pid)
{
if(execvp(arg[0], arg) < 0)
{
perror("error:");
}
}
else
{
sleep(0.1);
wait(NULL);
close(fd_in);
close(fd_half);
bool res = fileReverse();
}
}
dup2(out_save, STDOUT_FILENO);
dup2(in_save, STDIN_FILENO);
close(fd);
}
bool fileReverse()
{
int fd_read = open(file_half,O_RDONLY);
if(-1 == fd_read)
{
perror("open");
exit(1);
}
int fd_write = open(file_all,O_CREAT | O_WRONLY | O_TRUNC, 0664);
if(-1 == fd_write)
{
perror("create");
exit(1);
}
char *str = new char[1024];
int count = read(fd_read, str, sizeof(str));
if(-1 == count)
{
perror("read");
return false;
}
while(count)
{
write(fd_write, str, count);
count = read(fd_read, str, sizeof(str));
}
return true;
}
void file2Srcn()
{
int fd_read = open(file_all,O_RDONLY);
if(-1 == fd_read)
{
perror("open");
exit(1);
}
char *str = new char[1024];
int count = read(fd_read, str, sizeof(str));
if(-1 == count)
{
perror("read");
exit(1);
}
while(count)
{
write(STDOUT_FILENO, str, count);
count = read(fd_read, str, sizeof(str));
}
}
目前支持shell解析以及管道,但是重定向这种还没实现
实现方法不是最优,很蠢,大家发现问题欢迎下方留言讨论