上个月初的myshell实现让人大费脑筋,在字符串处理和多重管道上出了不少差错,但好在最后还是实现了要求的功能。真的太懒了,把博客拖到现在~
(之后还得补myls)
需求一个一个分析吧
实现管道 |
这个的意思就是想要你把一个命令的输出当作输入交给后面一个命令。如ls -l | grep “a” |wc -c就是把ls -l的输出结果交给grep 处理,grep的处理结果交给wc处理。
解决方案:fork+dup2(fd,0)+exec重定向输出到一个文件/tmp/file1中,fork+dup(fd,1)+exec再将这个文件temp/file1中当作输入。(其实应该用管道更好,但这里就照着书上的方式写了)
实现输入输出重定向 < > >>
- cat<a.txt 就是将 <后面的文件的内容给cat 当作标准输入。
- ls >a.txt 就是将 ls的输出覆盖写入a.txt中,也就是ls的标准输出给a.txt当标准输入
- ls>>a.txt 就是将ls的输入结果加在a.txt结尾,也就是ls的标准输出给a.txt(APPEND模式打开)当标准输入。
解决方案:依旧是dup2
实现后台 &
- 这里是书上所写的父进程wait(-1,NULL,WHOHANG)的非阻塞模式下父进程和子进程一起运行就"看上去"子进程在后台输出,其实这样算是父进程和子进程继续运行,然而的真正概念是后台程序会可能暂停。因为前台的程序肯定需要把握住终端的输入和输出,所以如果像ls这种写到终端的进程放在后台,它不应该同时输出在屏幕上,但是呢,还是有可能会写出来(可以使用sttytostop命令禁止),接着就是终端发送SIGTTOU信号,让会打印的程序暂停不去执行。同理需要标准输入的程序,则终端发送SIGTTIN信号,让需要写入的程序暂停不去执行。之后呢如果需要运行则是利用fg 命令,终端向程序发送sigcont,程序在前台继续运行。
但我写在这的就是书上那个非阻塞的wait啦
实现内置cd
嗯,就是chdir配上一些字符串操作。还或者需要下getcwd获取当前路径,目的是保存上次的路径用于"cd -","cd ~"则用下pwd=getpwuid(getuid())再pwd->pw_name
获取用户名。这样就可cd到home下的用户家目录了。
至于什么cd需要内置,是因为如果也是fork+exec(cd)的话cd这个程序实现里面的chdir只对cd命令的工作目录有效,却无法改变原程序的工作目录。
实现信号屏蔽
就暂时signal(SIGINT,SIG_IGN);
我觉得是不是忘了考虑SIGPIPE
实现多重管道
因为不可以把一个文件同时当作输入端和输出端(会出错),此处使用了俩文件/tmp/file1,/tmp/file2轮流来读写。如ls|grep a|wc进程 就是在子进程执行ls前,将标准输出重定向到file1,父进程再在新子进程执行grep前标准输出重定向到file2,标准输入重定向到file1,接着父进程再在执行新子进程执行wc前标准输出重定向到file1,标准输入重定向到file1。除了第一个子进程是输入端仍然是标准输入和最后一个子进程输出端仍是标准输出,其余均使用文件的重定向,说实话这样交替着是有点呆滞,其实FIFO,pipe实现父子进程间通信会好一些(之后我试试去修正)
可以在任何地方运行
就跟myls那里一样
任何位置运行myls
实现tab补全和内置history和光标移动
使用readline库(需下载)
#include<readline/readline.h>
#include<readline/history.h>
参考博文:readline1
(注:在读写history中有坑没处理好会死循环)
read_history(NULL)这条函数只用执行一次,放在程序开头即可。
编译的时候需要加上 -lreadline
成果:
源码shell_head.h文件
#ifndef SHELL_HEAD_H
#define SHELL_HEAD_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include<sys/mman.h>
#include<pthread.h>
#include<semaphore.h>
#include <netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/select.h>
#include<sys/epoll.h>
#include<unistd.h>
#include<dirent.h>
#include<pwd.h>
#include<grp.h>
#include<time.h>
#include<errno.h>
#include<signal.h>
#include<fcntl.h>
#include<stdbool.h>
#include<sys/wait.h>
#include<readline/readline.h>
#include<readline/history.h>
// #include<history.h>
#define COLOR_BAGIN "\001\033[1m\002"//强调,加粗,高亮
#define COLOR_END "\001\033[0m\002"
#define COLOR_BKG_SEF(x,y) "\001\033["#x";"#y"m\002"
#define COLOR_BKG(x) "\001\033["#x"m\002"//x<40-49>
#define COLOR_SEF(x) "\001\033["#x"m\002"//x<30-39>
#define DIRNAMESIZE 255
#define ARGMAXLEN 255
#define PIPE 1
#define INPUT 2
#define OUTPUT 4
#define APPPUT 8
#define BACKGROUND 16
#define EXIT 32
#define ARGMAXNUM 255
#define FILENAMESIZE 255
#define SHOWPATH 1
#define FILE1 "/tmp/file1"
#define FILE2 "/tmp/file2"
#define HISTORYFILE "historyfile"
#define HISTORYMAXSIZE 1000
struct history
{
int id;
char buf[ARGMAXLEN];
};
void get_prefix(char*str);
int find_2(char*String,char*str);
int get_mode(int*mode,char*input_str);
int back_check(char*str,int *mode);
char*s_gets(char*s,int n);
bool get_input(char*);
bool search_cmd(char*);
void execute_cmd(char**);
void print_shell();
void analysisArg(char**arg,int argcount,int *mode);
void sys_err(char*);
void split(char*subExpression,char**arglist,int*argcnt);
void split_2(char*String,char ch,char arglist[ARGMAXNUM][ARGMAXLEN],int *argNum);
int find(char*String,char c);
int do_arg(char*str,int mode);
void showdir(char*dirName);
void changdir(const char*newDir,char*oldDir);
char*format_blank(char*str);
void getUsername(char*username);
void getHostname(char*hostname);
int printHistory();
int getHistory(struct history *htylist,int *historylen);
int setHistory(const char*history);
void printHistory_2();
int setHistory_2(const char*input_str);
#endif
myshell.c文件
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include<sys/mman.h>
#include<pthread.h>
#include<semaphore.h>
#include <netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/select.h>
#include<sys/epoll.h>
#include<unistd.h>
#include<dirent.h>
#include<pwd.h>
#include<grp.h>
#include<time.h>
#include<errno.h>
#include<signal.h>
#include<fcntl.h>
#include"shell_head.h"
int main(int argc,char**argv)
{
read_history(NULL);
signal(SIGINT,SIG_IGN);
char oldDir[ARGMAXLEN];
memset(oldDir,0,ARGMAXLEN);
// char input_str[ARGMAXLEN];
while(1){
// print_shell();
char prefix_str[ARGMAXLEN];
char input_str[ARGMAXLEN];
get_prefix(prefix_str);
char*buf=readline(prefix_str);//COLOR_BAGIN COLOR_BKG_SEF(49,34)" " COLOR_END
// get_input(input_str);
strncpy(input_str,buf,ARGMAXLEN);
free(buf);
if(setHistory_2(input_str)==-1){
continue;
}
if(strcmp(input_str,"exit")==0||strcmp(input_str,"logout")==0){
exit(0);
}
else if(strncmp(input_str,"cd",2)==0&&((input_str[2]=='\0')||(input_str[2]==' '))){
char*s=format_blank(input_str+2);
changdir(s,oldDir);
continue;
}else if(strncmp(input_str,"history",7)==0&&((input_str[7]=='\0')||(input_str[7]==' '))){
printHistory_2();
continue;
}
int mode=0;
if(get_mode(&mode,input_str)!=0){
continue;
}
int ret2=do_arg(input_str,mode);
if(ret2!=0){
continue;
}
}
return 0;
}
#include"shell_head.h"
void sys_err(char*err)
{
if(err!=NULL)
fprintf(stderr,"%s\n",err);
// exit(0);
}
char*s_gets(char*s,int n)
{
char*find;
char*ret_val;
if(n>ARGMAXLEN){return NULL;}
ret_val=fgets(s,n,stdin);
if(ret_val){
find=strchr(s,'\n');
if(find){
*find='\0';
}else{
while(getchar()!='\n')
continue;
}
}
return ret_val;
}
void print_shell()
{
char hname[BUFSIZ];
char uname[BUFSIZ];
char dirname[BUFSIZ];
getHostname(hname);
getUsername(uname);
getcwd(dirname,BUFSIZ);
if(SHOWPATH){
printf(COLOR_BAGIN COLOR_BKG_SEF(41,33) "%s@%s"COLOR_END":"COLOR_BAGIN COLOR_BKG_SEF(46,33) "%s$ "COLOR_END,hname,uname,dirname);//40 ,34
}else{
printf(COLOR_BAGIN COLOR_BKG_SEF(41,33) "%s@%s"COLOR_END"$ ",hname,uname);
}
return;
}
void get_prefix(char*str)
{
char hname[BUFSIZ];
char uname[BUFSIZ];
char dirname[BUFSIZ];
getHostname(hname);
getUsername(uname);
getcwd(dirname,BUFSIZ);
if(SHOWPATH){
sprintf(str,COLOR_BAGIN COLOR_BKG_SEF(41,33) "%s@%s"COLOR_END":"COLOR_BAGIN COLOR_BKG_SEF(46,33) "%s$ "COLOR_END,hname,uname,dirname);
}else{
sprintf(str,COLOR_BAGIN COLOR_BKG_SEF(41,33) "%s@%s"COLOR_END"$ ",hname,uname);
}
}
bool get_input(char*argline)
{
if(s_gets(argline,ARGMAXLEN))
return 1;
else{
sys_err("get_arg");
return 0;
}
}
void split(char*subExpression,char**arglist,int*argcnt)
{
char*cur=subExpression;//ls -l\0
while(*cur==' '||*cur=='\t')
cur++;
char*begin=cur;
int argNum=0;
int len=0;
while(cur&&*cur!='\0'){
if(*cur==' '){
arglist[argNum]= malloc(ARGMAXLEN*sizeof(char));
memset(arglist[argNum],0,ARGMAXLEN);
strncpy(arglist[argNum],begin,len);
arglist[argNum][len]='\0';
len=0;
while(*cur==' '||*cur=='\t')
cur++;
begin=cur;
argNum++;
}else{
len++;
cur++;
}
}
arglist[argNum]= malloc(ARGMAXLEN*sizeof(char));
memset(arglist[argNum],0,ARGMAXLEN);
strncpy(arglist[argNum],begin,len);
arglist[argNum][len]='\0';
arglist[++argNum]=NULL;
*argcnt=argNum;
}
int back_check(char*str,int *mode)
{
char*f=strchr(str,'&');
char*p=f;
if(p){
p++;
while(*p!='\0'){
if(*p!=' '&&*p!='\t'){
*mode&=~BACKGROUND;
sys_err("& err");
return -1;
}
p++;
}
*f='\0';
*mode|=BACKGROUND;
return 0;
}else{
return 0;
}
}
bool search_cmd(char*cmd)
{
DIR*dp;
struct dirent*dirp;
char newcmd[ARGMAXLEN];
char*path[]={"./","/bin","/usr/bin",NULL,NULL};
char*find=strrchr(cmd,'/');
if(find){
char newpath[ARGMAXLEN];
strncpy(newpath,cmd,find-cmd+1);
int i;
for( i=0;path[i]!=NULL;i++){
}
path[i]=newpath;
strcpy(newcmd,find+1);
}else{
if(strncmp(cmd,"./",2)==0)
cmd+=2;
strcpy(newcmd,cmd);
}
// printf("n=%s\n",newcmd);
for (int i = 0; path[i]!=NULL; i++)
{
if((dp=opendir(path[i]))==NULL)
sys_err("cant open \n");
while((dirp=readdir(dp))!=NULL){
// printf("d=%s\n",dirp->d_name);
if(strcmp(dirp->d_name,newcmd)==0){
closedir(dp);
return 1;
}
}
closedir(dp);
}
return 0;
}
void split_2(char*String,char ch,char arglist[ARGMAXNUM][ARGMAXLEN],int *argNum)
{
char*p=String;
char*begin=p;
*argNum=0;
int len=0;
while(*p!='\0'){
if(*p==ch){
if(len!=0){
strncpy(arglist[(*argNum)],begin,len);
arglist[(*argNum)++][len]='\0';
}
if(*argNum==ARGMAXNUM)
sys_err("too many arg");
p++;
begin=p;
len=0;
}else{
len++;
p++;
}
}
if(len!=0){
strncpy(arglist[(*argNum)],begin,len);
arglist[(*argNum)++][len]='\0';
}
}
int find(char*String,char c)
{
int cnt=0;
char*p=String;
int len=0;
while(p!=NULL&&*p!='\0'){
if(*p==c&&(*(p+1)!=c)){
if(len>=1&&*(p-1)==c){
p++;
len++;
continue;
}
if((p+1!=NULL)&&(*(p+1)==c)){
p++;
len++;
continue;
}
cnt++;
}
p++;
len++;
}
return cnt;
}
int find_2(char*String,char*str)
{
// llllllllll<< <<\0
int cnt=0;
char*p=String;
int StrLen=strlen(String);
int strLen=strlen(str);
while(p&&*p!='\0'){
if(strncmp(p,str,strLen)==0){
cnt++;
}
p++;
}
return cnt;
}
int do_arg(char*str,int mode)
{
// printf("%d\n",mode);
char arglist2[ARGMAXNUM][ARGMAXLEN];
int argNum2=0;
char fileName[FILENAMESIZE];
if(mode&INPUT){
split_2(str,' ',arglist2,&argNum2);
char*arg[argNum2+1];
for(int j=0;j<argNum2;j++){
arg[j]=(char*)arglist2[j];
}
arg[argNum2]=NULL;
int i;
for(i=0;i<argNum2;i++)
if(strcmp(arg[i],"<")==0&&i==argNum2-2&&i!=0){
strncpy(fileName,arglist2[i+1],FILENAMESIZE);
arg[i]=NULL;
break;
}
int fd=open(fileName,O_RDONLY);
if(fd==-1){
perror("open");
// sys_err("open");
return -2;
}
int pid=fork();
if(pid==-1){
sys_err("fork");
return -3;
}else if(pid==0){
dup2(fd,0);
if(search_cmd(arg[0])==false){
sys_err("找不到文件");
exit(0);
}
if(execvp(arg[0],arg)==-1){
sys_err("执行失败!!!");
exit(0);
}
}else{
if(mode&BACKGROUND)
printf("pid[%d]正在后台运行\n",pid);
else
waitpid(pid,NULL,0);
return 0;
}
}else if(mode&OUTPUT||mode&APPPUT){
split_2(str,' ',arglist2,&argNum2);
char*arg[argNum2+1];
for(int j=0;j<argNum2;j++){
arg[j]=(char*)arglist2[j];
}
arg[argNum2]=NULL;
int i;
for(i=0;i<argNum2;i++)
if((strcmp(arg[i],">")==0||strcmp(arg[i],">>")==0)&&i==argNum2-2&&i!=0){
strncpy(fileName,arglist2[i+1],FILENAMESIZE);
arg[i]=NULL;
break;
}
int fd;
if(mode&OUTPUT)
fd=open(fileName,O_CREAT|O_TRUNC|O_RDWR,0644);
else if(mode&APPPUT)
fd=open(fileName,O_CREAT|O_APPEND|O_RDWR,0644);
if(fd==-1){
perror("open");
// sys_err("open");
return -2;
}
int pid=fork();
if(pid==-1){
sys_err("fork");
return -3;
}else if(pid==0){
dup2(fd,1);
if(search_cmd(arg[0])==false){
sys_err("找不到文件");
exit(0);
}
if(execvp(arg[0],arg)==-1){
sys_err("执行失败!!!");
exit(0);
}
}else{
if(mode&BACKGROUND)
printf("pid[%d]正在后台运行\n",pid);
else
waitpid(pid,NULL,0);
return 0;
}
}else if((!(mode&PIPE))&&(!(mode&OUTPUT))&&(!(mode&INPUT))){
split_2(str,' ',arglist2,&argNum2);
char*arg[argNum2+1];
for(int j=0;j<argNum2;j++){
arg[j]=(char*)arglist2[j];
}
arg[argNum2]=NULL;
int pid=fork();
if(pid==-1){
sys_err("fork");
return -3;
}else if(pid==0){
if(search_cmd(arg[0])==false){
sys_err("找不到文件");
exit(0);
}
if(execvp(arg[0],arg)==-1){
sys_err("执行失败!!!");
exit(0);
}
}else{
if(mode&BACKGROUND)
printf("pid[%d]正在后台运行\n",pid);
else
waitpid(pid,NULL,0);
return 0;
}
}else if(mode&PIPE){
split_2(str,'|',arglist2,&argNum2);
char arglist3[ARGMAXNUM][ARGMAXLEN];
char infile[FILENAMESIZE];
char outfile[FILENAMESIZE];
for(int i=0;i<argNum2;i++){
int argNum3=0;
if(i%2){
strcpy(infile,FILE1);
strcpy(outfile,FILE2);
}else{
strcpy(infile,FILE2);
strcpy(outfile,FILE1);
}
split_2(arglist2[i],' ',arglist3,&argNum3);
char*arg[argNum3+1];
for(int j=0;j<argNum3;j++){
arg[j]=(char*)arglist3[j];
}
arg[argNum3]=NULL;
int pid=fork();
if(pid==-1){
sys_err("fork");
return -3;
}else if(pid==0){
if(search_cmd(arg[0])==false){
sys_err("找不到文件");
exit(0);
}
if(i!=0){
int fd=open(infile,O_RDONLY);
// printf("i=%d\tinfile:%s:",i,infile);
if(fd==-1){
perror("open");
// sys_err("open");
return -2;
}
dup2(fd,0);
// rmdir("/tmp/mystery love");
}
// printf("%d--argNUM3:\n",argNum2);
if(i!=argNum2-1){
// printf("i=%d,outfile:%s:",i,outfile);
int fd2=open(outfile,O_CREAT|O_TRUNC|O_RDWR,0644);
if(fd2==-1){
sys_err("open");
return -2;
}
dup2(fd2,1);
}
if(execvp(arg[0],arg)==-1){
sys_err("执行失败!!!");
exit(0);
}
}else{
// dup2(fd,1);
if(mode&BACKGROUND)
printf("pid[%d]正在后台运行\n",pid);
else
waitpid(pid,NULL,0);
// return 0;
remove(infile);
}
}
}
}
int get_mode(int*mode,char*input_str)
{
int ret=back_check(input_str,mode);
if(ret==-1){
return -1;
}
if(find(input_str,'|')){
if(find_2(input_str,">>")||find(input_str,'>')||find(input_str,'<')){
sys_err(" | 下不支持>,<,>>");
return -1;
}else
*mode|=PIPE;
}else{
int ocnt=find(input_str,'>');
int icnt=find(input_str,'<');
int acnt=find_2(input_str,">>");
// printf("%d%d%d\n",ocnt,icnt,acnt);
if(ocnt>1){
sys_err("只支持1个>");
return -1;
}
if(icnt>1){
sys_err("只支持1个<");
return -1;
}
if(acnt>1){
sys_err("只支持1个<<");
return -1;
}
if((ocnt==icnt&&icnt==1)||(ocnt==acnt&&acnt==1)||(icnt==acnt&&acnt==1)){
sys_err("不可同时支持< > >>");
return -1;
}
if(ocnt==1){
*mode|=OUTPUT;
}else if(icnt==1){
*mode|=INPUT;
}else if(acnt==1){
*mode|=APPPUT;
}
}
return 0;
}
void changdir(const char*newDirName,char*oldDirName)
{
char newDir[ARGMAXLEN];
strcpy(newDir,newDirName);
if(strcmp(newDir,"-")==0){
if(*oldDirName=='\0'){
sys_err("这是第一次跳转目录,因此无法跳转到上一次的目录");
}else{
strcpy(newDir,oldDirName);
}
}else if(strcmp(newDir,"~")==0){
char uname[BUFSIZ];
getUsername(uname);
sprintf(newDir,"/home/%s",uname);
}
getcwd(oldDirName,ARGMAXLEN);
if(chdir(newDir)==-1){
fprintf(stderr,"错误的目录名称:%s\n",newDir);
return ;
}
}
void showdir(char*dirName)
{
getcwd(dirName,DIRNAMESIZE);
printf("%s\n",dirName);
}
char*format_blank(char*str)
{
if(str==NULL)return str;
int len=strlen(str);
char*p=str,*e=str+len;
while(p&&(*p!='\0')){
if(*p==' '||*p=='\t'){
p++;
}else{
break;
}
}
while(e&&*e!=*p){
if(*e==' '||*e=='\t'){
*e='\0';
e--;
}else{
break;
}
}
return p;
}
void getUsername(char*username)
{
struct passwd* pwd = getpwuid(getuid());
strcpy(username, pwd->pw_name);
}
void getHostname(char*hostname)
{
gethostname(hostname, BUFSIZ);
}
int setHistory(const char*input_str)
{
if(input_str==NULL||*input_str=='\0')
return -1;
int historylen=0;
struct history htylist[HISTORYMAXSIZE];
char history[ARGMAXLEN];
strcpy(history,input_str);
for(int i=0;i<HISTORYMAXSIZE;i++)
memset(htylist+i,0,sizeof(struct history));
if(getHistory(htylist,&historylen)!=-1){
if(historylen==0){
htylist[historylen].id=0;
}
else{
htylist[historylen].id=htylist[historylen-1].id+1;
}
strncpy(htylist[historylen].buf,history,HISTORYMAXSIZE);
int fd=open(HISTORYFILE,O_APPEND|O_RDWR);
if(fd==-1){
perror("open");
return -1;
}
int wlen=write(fd,htylist+historylen,sizeof(struct history));
if(wlen==-1)
fprintf(stderr,"%s\n",strerror(errno));
close(fd);
return 0;
}else{
return -1;
}
}
int getHistory(struct history *htylist,int *historylen)
{
int fd=open(HISTORYFILE,O_CREAT|O_RDONLY,0644);
if(fd==-1){
perror("open");
return -1;
}
else {
int ret=0;
int i=0;
while(ret=read(fd,htylist+i,sizeof(struct history))){
if(ret==-1){
fprintf(stderr,"read err %s",strerror(errno));
return -1;
}if(ret==0){
break;
}
i++;
(*historylen)++;
if(i==HISTORYMAXSIZE){
for(int j=0;j<HISTORYMAXSIZE;j++)
memset(htylist+j,0,sizeof(struct history));
i=0;
*historylen=0;
}
}
}
close(fd);
return 0;
}
int printHistory()
{
struct history htylist[HISTORYMAXSIZE];
int htylen=0;
if(getHistory(htylist,&htylen)==-1){
return -1;
}
for(int i=0;i<htylen;i++){
printf("%d %s\n",htylist[i].id,htylist[i].buf);
}
return 0;
}
int setHistory_2(const char*input_str)
{
if(input_str!=NULL&&*input_str!='\0'){
add_history(input_str);
write_history(NULL);
return 0;
}else{
return -1;
}
}
void printHistory_2()
{
int i = 0;
HIST_ENTRY ** his;
his = history_list();
while(his[i] != NULL)
{
printf("%d %s\n",i, his[i]->line);
i++;
}
}
本文其实有很多漏洞和不足之处也比较冗长(总让我感觉很多地方可以用什么方法复用)。本文也未实现多重管道和输入输出重定向的混搭,因为一开始在此处的字符串处理上卡壳太久(心累)。
有些函数是开始的版本的就比如main中有使用xxxx_2的函数,本来在用xxxx,我没有删去。保留下来康康挺心酸的。
那些写的不优雅的地方之后会慢慢矫正吧,我想。