自己动手实现linux-->Shell
在开始代码之前,先来普及一下什么是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在计算机硬件和软件中的位置
-
直接上代码.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324#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));
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;
}
本文转自asd1123509133 51CTO博客,原文链接:http://blog.51cto.com/lisea/1788046,如需转载请自行联系原作者