功能概述
一个简单的shell程序。实现了基本功能,包括一些内部命令,所有外部命令,支持标准IO重定向和管道。但是IO重定向和管道只能适用于简单的情况。
平台: Ubuntu14.04 </br>
运行MyShell即可进入shell,会出现[MyShell] zdf@zdf-virtual-machine:~/MyShell$
类似这样的提示
支持的内部命令
- cd 与linux下的cd功能相同: </br>cd .. 返回上一级目录 </br>cd 进入个人的主目录 </br>cd ~/directory 进入个人主目录下的目录 </br>cd directory 进入目录
- exit/quit 退出shell
实现此部分所使用的系统调用有getpwuid
、getuid
和chdir
。
支持所有外部命令
支持所有的外部命令,且支持参数
实现此部分所使用的系统调用有execvp
。
支持标准IO重定向
只能对第一条命令做重定向
实现此部分所使用的系统调用有open
和dup2
。
支持管道
只能通过管道连接两个命令,例如ls -l | sort
。所以一条命令中只能有一个|
。
实现此部分所使用的系统调用有fork()
、pipe()
和dup2
。
其他
当输出重定向与管道同时存在时,管道命令只能放在最后,例如ls -l > tmp | sort
。但是此时管道已经没有意义了。所以不建议输出重定向与管道共用。
源代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <pwd.h>
#include <fcntl.h>
#define Max_Name_Len 256
#define Max_Path_Len 1024
#define Maxn 100
char *argv[Maxn];
int len;
int fd[2];
void type_prompt() {
char hostname[Max_Name_Len];
char pathname[Max_Path_Len];
struct passwd *pwd;
pwd = getpwuid(getuid());
gethostname(hostname, Max_Name_Len);
printf("[MyShell] %s@%s:", pwd -> pw_name, hostname);
getcwd(pathname, Max_Path_Len);
if (strncmp(pathname, pwd -> pw_dir, strlen(pwd -> pw_dir)) == 0) {
printf("~%s", pathname + strlen(pwd -> pw_dir));
}
else {
printf("%s", pathname);
}
if (geteuid() == 0) {
printf("#\n");
}
else {
printf("$\n");
}
}
void read_command()
{
char ch = ' ';
int i = 0;
memset(argv, 0, sizeof(argv));
while (ch != '\n') {
char s[Maxn]; // the command length should be less than Maxn
scanf("%s%c", s, &ch);
argv[i] = (char*)malloc(sizeof(s));
strcpy(argv[i++], s);
}
len = i;
}
void RunOutterFunction(int offset)
{
int ret;
ret = execvp(argv[offset], argv + offset);
if (ret < 0) {
printf("command not found\n");
}
}
int RunInnerFunction()
{
struct passwd *pwd;
char *cd_path = NULL;
if (strcmp(argv[0], "exit") == 0 || strcmp(argv[0], "quit") == 0) // exit
exit(0);
if (strcmp(argv[0], "cd") == 0) {
pwd = getpwuid(getuid());
if (argv[1] == NULL) { // cd
cd_path = malloc(strlen(pwd -> pw_dir));
strcpy(cd_path, pwd -> pw_dir);
}
else if (argv[1][0] == '~') { // cd ~/...
cd_path = malloc(strlen(pwd -> pw_dir) + strlen(argv[1]));
strcpy(cd_path, pwd -> pw_dir);
strcpy(cd_path + strlen(pwd -> pw_dir), argv[1] + 1);
}
else { // normal
cd_path = malloc(strlen(argv[1] + 1));
strcpy(cd_path, argv[1]);
}
if (chdir(cd_path) != 0) {
printf("-MyShell: cd: %s:%s\n", cd_path, strerror(errno));
}
free(cd_path);
return 1;
}
return 0;
}
void Redirect()
{
int i;
for (i = 1; i < len; ++i)
if (argv[i] != NULL){
if (strcmp(argv[i], ">") == 0) {
int output = open(argv[i + 1], O_WRONLY | O_CREAT | O_TRUNC, 0666);
dup2(output, 1);
close(output);
argv[i] = NULL;
++i;
continue;
}
if (strcmp(argv[i], ">>") == 0) {
int output = open(argv[i + 1], O_WRONLY | O_APPEND | O_CREAT, 0666);
dup2(output, 1);
close(output);
argv[i] = NULL;
++i;
continue;
}
if (strcmp(argv[i], "2>") == 0) {
int output = open(argv[i + 1], O_WRONLY | O_CREAT | O_TRUNC, 0666);
dup2(output, 2);
close(output);
argv[i] = NULL;
++i;
continue;
}
if (strcmp(argv[i], "2>>") == 0) {
int output = open(argv[i + 1], O_WRONLY | O_APPEND | O_CREAT, 0666);
dup2(output, 2);
close(output);
argv[i] = NULL;
++i;
continue;
}
if (strcmp(argv[i], "<") == 0) {
int input = open(argv[i + 1], O_CREAT | O_RDONLY, 0666);
dup2(input, 0);
close(input);
argv[i] = NULL;
++i;
continue;
}
}
}
int hasPipe()
{
int i;
for (i = 1; i < len; ++i)
if (argv[i] != NULL && strcmp(argv[i], "|") == 0) {
argv[i] = NULL;
return i + 1;
}
return 0;
}
void Pipe(int x)
{
int pid2;
if (x == 0) return;
if ((pid2 = fork()) == 0) {
//sub process
close(fd[1]);
dup2(fd[0], 0);
RunOutterFunction(x);
}
else {
//parent process
close(fd[1]);
waitpid(pid2, NULL, 0);
}
}
int main()
{
int pid, flag;
while (1) {
type_prompt();
read_command();
if (RunInnerFunction())
continue;
if (flag = hasPipe())
pipe(fd);
if ((pid = fork()) == 0) {
//sub process
if (flag) {
close(fd[0]);
dup2(fd[1], 1);
}
Redirect();
RunOutterFunction(0);
break;
}
else if (pid > 0) {
//parent process
Pipe(flag);
waitpid(pid, NULL, 0);
}
else {
printf("fork failed.");
break;
}
}
return 0;
}