#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解析以及管道,但是重定向這種還沒實現
實現方法不是最優,很蠢,大家發現問題歡迎下方留言討論