分享一下去年修作業系統的時候寫出來的shell(最終成績97,因為做不到多重檔案重定向被扣了三分),shell主要結構大概是這樣,沒記錯的話,這也是恐龍書裡面的架構,接下來要做的事情僅僅只是依據此架構進行延伸。

while (TRUE){
type_prompt();
read_command(command, parameters);

if (fork() != 0)
{
waitpid(-1, &status, 0);
}
else
{
execvp(command, parameters);
}
}

將程式碼初步補充完之後,可以得到下面的架構,這部分是初步的完成品,也是我覺得很好的學習範例。

目前能夠執行基礎的指令,但不能實現pipe檢測和檔案重定向等功能。

#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <vector>

#define TRUE 1

using namespace std;

void type_prompt();
void read_command(char *, char **);
const int MAX_LENGTH = 256;
string local = "~\\";

int main()
{
char *command = new char[MAX_LENGTH];
char **parameters = new char *[MAX_LENGTH];
int status = 0;

//進入畫面
cout << "Welcome to AfanShell" << endl;
cout << "ctrl-c or ctrl-\\ to quit" << endl;

while (TRUE)
{
type_prompt();
read_command(command, parameters);

if (fork() != 0)
{
waitpid(-1, &status, 0);
}
else
{
execvp(command, parameters);
}
}

return 0;
}

void type_prompt()
{
cout << "AfanShell@"
<< "> ";
}

void read_command(char *command, char **parameters)
{
char *tmp = new char[MAX_LENGTH];
char *token;
// char *pipe = "|"; //檢測pipe功能啦
int count = 0;
int i = 0;

for (i = 0; i < MAX_LENGTH; i++)
{
parameters[i] = NULL;
}

cin.getline(tmp, MAX_LENGTH - 1);

token = strtok(tmp, " ");
while (token != NULL) {
parameters[count] = new char[MAX_LENGTH];
strcpy(parameters[count], token);
token = strtok(NULL, " ");
count++;
if (count == MAX_LENGTH - 1) {
break;
}
}

if (count == 0) {
parameters[0] = new char[MAX_LENGTH];
strcpy(parameters[0], "echo");
parameters[1] = new char[MAX_LENGTH];
strcpy(parameters[1], "No input received");
}

strcpy(command, parameters[0]);
}

最終成果如下,將pipe補上,但要注意的是不可以做多重檔案重定向。

另外將程式碼改成c,雖然改動範圍不大就是了,希望對大家有幫助。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

/**
* author Afan Chen.
*/

static char *args[512];
pid_t pid;
int command_pipe[2];

#define READ 0
#define WRITE 1

int execute(char *cmd, int input, int first, int last);
void WaitChild(int n);
char line[500];
int n = 0;

void type_prompt();
int read_command(int input, int first, int last);

int main()
{
printf("Hello!\nThis is Afan@Shell\nctrl-c or ctrl-\\ to quit\n");
while (true)
{
type_prompt();

if (!fgets(line, 1024, stdin))
return 0;

int input = 0;
int first = 1;

char *cmd = line;
char *next = strchr(cmd, '|'); /* 找第一個 '|' */
const char *file = "<>";

if (strpbrk(cmd, file) != NULL)
{
system(cmd);
continue;
}

while (next != NULL)
{
*next = '\0';
input = execute(cmd, input, first, 0);

cmd = next + 1;
next = strchr(cmd, '|'); /* 找其他的 '|' */
first = 0;
}
input = execute(cmd, input, first, 1);
WaitChild(n);
n = 0;
}
return 0;
}

void type_prompt()
{
printf("Afan@Shell:$ ");
fflush(NULL);//清空緩衝區,把字串print出來
}

int read_command(int input, int first, int last)
{
int pipettes[2];

pipe(pipettes);
pid = fork();

if (pid == 0)
{
if (first == 1 && last == 0 && input == 0)
{
dup2(pipettes[WRITE], STDOUT_FILENO);
}
else if (first == 0 && last == 0 && input != 0)
{
dup2(input, STDIN_FILENO);
dup2(pipettes[WRITE], STDOUT_FILENO);
}
else
{
dup2(input, STDIN_FILENO);
}

if (execvp(args[0], args) == -1){
printf("OOOOOOOh!It's wrong.\n");
_exit(EXIT_FAILURE);
}
}

if (input != 0)
close(input);

close(pipettes[WRITE]);

if (last == 1)
close(pipettes[READ]);

return pipettes[READ];
}

void WaitChild(int n)
{
int i;
for (i = 0; i < n; i++)
wait(NULL);
}

char* SkipWhite(char *s)
{
while (isspace(*s))
s++;
return s;
}

void Split(char *cmd)
{
cmd = SkipWhite(cmd);
char *next = strchr(cmd, ' ');
int i = 0;

while (next != NULL)
{
next[0] = '\0';
args[i] = cmd;
i++;
cmd = SkipWhite(next + 1);
next = strchr(cmd, ' ');
}

if (cmd[0] != '\0')
{
args[i] = cmd;
next = strchr(cmd, '\n');
next[0] = '\0';
i++;
}

args[i] = NULL;
}

int execute(char *cmd, int input, int first, int last)
{
Split(cmd);
if (args[0] != NULL)
{
n += 1;
return read_command(input, first, last);
}
return 0;
}