在开始代码之前,先来普及一下什么是Shell. Shell英文又称壳层.在计算机中,是指"提供用户使用界面"的"系统"软件,通常指的是命令行界面的命令解析器.一般来说,这个词是指操作系统中,提供访问内核所提供之服务的程序.不过这个词也拿来指应用软件,或是任何在特定组件外围的"软件"...省略300字.....
常用的Shell分类:
-
bash: 是GNU的Bourne Again Shell,是GNU操作系统上默认的Shell
-
Korn Shell: 是对Bourne Shell的发展,在大部分内容上与Bourne Shell(Bash)兼容
-
C Shell: 是SUN公司Shell的BSD版本
-
Z Shell: Z是最后一个字母,也就是终极Shell.它集成了Bash ksh的重要特性,同时又增加了自己独有的特性.
来看看Shell在计算机的位置和用途
直接上代码.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
typedef struct __Info{
char buf[1024]; //用户输入信息
unsigned int pipeNum; //统计管道的个数
}Info;
//记录主机名 默认localhost
static char hostname[1024] = "localhost";
//错误提示
int sys_err(const char* err){
perror(err);
exit(EXIT_FAILURE);
}
//消息提示
void display(){
static unsigned int sum = 1; //记录输入次数
char buf[1024] = { 0 };
//获取当前路径
getcwd(buf, sizeof(buf));
printf("[#%d#%s@%s]>", sum, hostname, buf);
sum++;
}
/*
* 用户输入的内容处理
* @reminder info 传入的结构体
* return:
* 成功:EXIT_SUCCESS
* 失败:EXIT_FAILURE
*/
int handle(Info* info){
if(NULL == info){
printf("func %s err: [NULL == info]\n", __FUNCTION__);
exit(EXIT_FAILURE);
}
char* p = info->buf;
//统计管道的个数
while(NULL != (p = strchr(p, '|'))){
info->pipeNum++;
p++;
}
p = info->buf;
//去除回车
if(NULL != (p = strchr(p, '\n'))){
*p = '\0';
}
return EXIT_SUCCESS;
}
//字符替换
int replate(char* str, const char src, const char des){
if(NULL == str){
printf("func %s error: [NULL == str]\n", __FUNCTION__);
exit(EXIT_FAILURE);
}
char* p =str;
while(*p){
if(src == *p){
*p = des;
}
p++;
}
return EXIT_SUCCESS;
}
//命令解析
int resolveRun(char* ptr, char** argv){
if(NULL == ptr || NULL == argv){
printf("func %s error:[NULL == ptr || NULL == argv]\n", __FUNCTION__);
exit(EXIT_FAILURE);
}
int i = 0;
int fd;
char* inptr = NULL;
char* p = strtok_r(ptr, " ", &inptr);
argv[i++] = p;
while(NULL != (p = strtok_r(NULL, " ", &inptr))){
argv[i++] = p;
}
//判断是否有重定向
i--;
while(i){
if(0 == strcmp(argv[i], ">")){
fd = open(argv[i+1], O_WRONLY | O_CREAT | O_TRUNC, 0644);
if(0 > fd){
sys_err("func resolveRun() open error: ");
}
//备份输出至1023描述符
dup2(STDOUT_FILENO, 1023);
dup2(fd, STDOUT_FILENO);
argv[i] = NULL;
close(fd);
//找到第一个>
int n = 1;
while(n < i){
if(0 == strcmp(argv[n], ">")){
argv[n] = NULL;
break;
}
n++;
}
break;
}
if(0 == strcmp(argv[i], ">>")){
fd = open(argv[i+1], O_APPEND|O_CREAT|O_WRONLY, 0644);
if(0 > fd){
sys_err("func resolveRun() open error: ");
}
dup2(STDOUT_FILENO, 1023);
dup2(fd, STDOUT_FILENO);
argv[i] = NULL;
close(fd);
//找到第一个>
int n = 1;
while(n < i){
if(0 == strcmp(argv[n], ">>")){
argv[n] = NULL;
break;
}
n++;
}
break;
}
if(0 == strcmp(argv[i], "<")){
fd = open(argv[i+1], O_RDONLY);
if(0 > fd){
sys_err("func resolveRun() open error: ");
}
char buf[1024] = { 0 };
int len = 0;
while(0 != (len = read(fd, buf, sizeof(buf)))){
if(0 > len){
sys_err("func resolveRun() read error: ");
}
write(STDIN_FILENO, buf, len);
bzero(buf, sizeof(buf));
}
//向末尾输出一个结束符-1
putc(-1, STDIN_FILENO);
argv[i] = NULL;
close(fd);
//找到第一个>
int n = 1;
while(n < i){
if(0 == strcmp(argv[n], "<")){
argv[n] = NULL;
break;
}
n++;
}
break;
}
i--;
}
return EXIT_SUCCESS;
}
//拆分命令
int seve(Info* info){
if(NULL == info){
printf("func %s err: [NULL == info]\n", __FUNCTION__);
exit(EXIT_FAILURE);
}
//判断是否为空数据
if(0 == *(info->buf)){
return EXIT_SUCCESS;
}
pid_t pid;
pid_t wpid;
char buf[1024] = { 0 };
char* p = buf;
char* inptr = NULL;
int i = 0;
int fd[2];
char* argv[256] = {NULL};
//复制原有数据
memcpy(buf, info->buf, sizeof(buf));
//处理tab键
replate(buf, '\t', ' ');
//处理'号
replate(buf, '\'', ' ');
//处理"号
replate(buf, '\"', ' ');
for(i = 0; i <= info->pipeNum; i++){
if(0 == i){
p = strtok_r(p, "|", &inptr);
}else{
p = strtok_r(NULL, "|", &inptr);
}
//初始化
bzero(argv, sizeof(argv));
//命令解析
resolveRun(p, argv);
//判断是否是内置命令
if(0 == i && 0 == strcmp("cd", argv[0])){
if(0 > chdir(argv[1])){
//判断错误类型,提示用户信息
if(ENOENT == errno){
printf("-sea_bash: cd: %s: 没有那个文件或目录\n", argv[1]);
}
if(EACCES == errno){
printf("-sea_bash: cd: %s: 权限不够\n", argv[1]);
}
if(ENOTDIR == errno){
printf("-sea_bash: cd: %s: 不是目录\n", argv[1]);
}
}
return EXIT_SUCCESS;
}
else if(0 == i && 0 == strcmp("pwd", argv[0])){
char buf[1024] = { 0 };
getcwd(buf, sizeof(buf)); //获取当前工作目录
buf[strlen(buf)] = '\n'; //末尾添加换行
write(STDOUT_FILENO, buf, strlen(buf)); //向屏幕打印当前路径
return EXIT_SUCCESS; //成功结束
}
else if(0 == i && 0 == strcmp("hostname", argv[0])){
//清空
bzero(hostname, sizeof(hostname)); //将原来的hostname清空
memcpy(hostname, argv[1], strlen(argv[1])); //重新设置hostname
return EXIT_SUCCESS;
}
else if(0 == i && 0 == strcmp("exit", argv[0])){
//结束进程
printf("--------------------god-bey-----------------------------\n");
kill(getpid(), SIGINT); //向本进程发送结束信号
exit(EXIT_SUCCESS); //直接进程退出
}
//创建管道
if(0 > pipe(fd)){
sys_err("func seve() pipe error: ");
}
pid = fork();
if(0 > pid){
sys_err("func seve() fork error:");
}else if(0 == pid){
close(fd[0]); //子进程关闭读端
dup2(1022, fd[0]); //将上一个管道的读端重定向到fd[0]
close(1022); //关闭1022上一个信号的读端,避免多个读端存在
break; //跳出
}
//还原输出描述符
dup2(1023, STDOUT_FILENO);
//保存读端给下一个进程使用
dup2(fd[0], 1022);
close(fd[1]);
close(fd[0]);
}
//子进程处理
if(i != info->pipeNum+1){
//没有管道命令
if(0 != info->pipeNum){
if(i == info->pipeNum){
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
}
if(0 == i){
dup2(fd[1], STDOUT_FILENO);
}
else{
dup2(fd[0], STDIN_FILENO);
dup2(fd[1], STDOUT_FILENO);
}
}
execvp(argv[0], argv);
printf("-sea_bash: %s: command not found\n", argv[0]);
exit(EXIT_FAILURE);
}
//父进程等待子进程结束
if(i == info->pipeNum+1){
do{
wpid = waitpid(-1, NULL, WNOHANG);
if(0 < wpid){
i--;
}
}while(0 < i);
}
return EXIT_SUCCESS;
}
int main(int argc, char* argv[]){
Info info = {{0}, 0};
for(;;){
display();
//获取用户输入
fgets(info.buf, sizeof(info.buf), stdin);
//信息处理
handle(&info);
//拆分命令
seve(&info);
//清空初始化
bzero(&info, sizeof(info));
}
return EXIT_SUCCESS;
}