一.myshell的功能:
1. ls
2. 輸入輸出重定向
3. 管道操作
4. linux的內置命令 cd
5. 簡單的history
6. 屏蔽ctrl + c
二.主要難點:
1.命令解析
2.進程的應用
3.cd命令的輸出格式
三.代碼:
#include<stdio.h>
# include <sys/types.h>
# include <unistd.h>
# include <sys/wait.h>
# include <stdlib.h>
# include <string.h>
# include <dirent.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <linux/limits.h>
# include <errno.h>
# include <signal.h>
# define P 3 // 管道
# define I 2 // 輸入重定向
# define V 4 // 兩個>>輸出流重定向
# define O 1 // 輸出重定向
# define N 0 // 一般的命令
void output( char argv[][256], int sum );
void analyse(int , char fen[][256]);//執行參數
void fenge( char *buf, char fen[][256], int *len );
int find_mingli(char *canshu[256]);//一次只傳進來一個,所以用一維的
void my_error( const char * err_string, int line )
{
fprintf(stderr,"line : %d\n", line);
perror( err_string );
exit(-1);
}
void my_cd(char fen[][256], char str[256], int len)
{
char buf[256]="/home/zongjin/";
char *arg2[256];
int i;
for( i = 0; i<len; i++ )
{
arg2[i] = (char*)fen[i];
arg2[i][strlen(arg2)] = '\0';
}
arg2[i] = NULL;
if(arg2[1] == NULL)
{
chdir(buf);
strcpy(str,buf);
str[strlen(str)]='\0';
}
if( arg2[1] != NULL )
{
chdir(arg2[1]);
strcpy(str,arg2[1] );
str[strlen(str)]='\0';
}
}
void xinhao(int signo, sigset_t * newmask, sigset_t * oldmask)
{
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
}
sigset_t newmask, oldmask;
int main(void)
{
int i=0, len,j;
char buf[256];
char argv[100][256];
char fen[100][256];
char str[256]= "mashell:";
int fd;
//屏蔽ctrl - c
sigemptyset(&newmask);//建立一個信號箱
sigaddset(&newmask, SIGINT);//將ctrl+c的信號放到信號箱內
while(1)
{
len =0;
printf("%s:",str);
signal(SIGINT, xinhao);//調用函數xinhao
gets(argv[i]);
strcpy(buf, argv[i]);
buf[strlen(buf)] = '\0';
if( (strcmp(buf, "exit") ==0 ) || (strcmp(buf, "logout" ) == 0))
{
break;
}
i++;
if( strcmp( buf, "history" ) == 0 )
{
output(argv, i);
}
fenge(buf, fen, &len);
if( strcmp( fen[0], "cd" ) == 0 )
{
strcpy( str, "mashell:");
char str2[256];
my_cd(fen, str2,len);
strcat( str,str2 );
str[strlen(str)]='\0';
str2[256]=NULL;
continue;
}
analyse(len, fen);
}
exit(0);
}
void output( char argv[100][256], int sum )
{
int i;
char buf[256];
for( i = 0; i<sum; i++ )
{
printf("%d:%s\n",i+1,argv[i]);
}
}
//命令解析,以空格進行分割
void fenge( char * buf, char fen[100][256], int * len )
{
int i, j=0;
char p[256];//這塊不能寫成char *p的形式,否則出現段錯誤
for( i = 0; buf[i]!= NULL; i++ )
{
p[j] = buf[i];
j++;
if( buf[i] == ' ')
{
strcpy(fen[*len], p );
fen[*len][j-1]= '\0';
j=0;
*len = *len +1;
}
if( buf[i+1] == '\0' )
{
strcpy(fen[*len], p);
fen[*len][j]= '\0';
*len= *len +1;// 這塊不能寫成*len++這樣len的值不變
j=0;
}
}
}
//分析命令
void analyse(int len, char fen[100][256])
{
int i,fd, status, pid2,fd2,ccc;
int t=0;//收納各種符號
int sum;//以空格記錄命令的個數
int hou;//單個記錄後臺符的個數,是一個輔助命令,所以單個記錄下來
int flag=0;//記錄各種輸入的各種符號的個數
pid_t pid;
pid_t child_pid;
char *file;
char *arg[256];
char *str = "cd";
char* argnext[len+1];
sum = len;
hou = 0;
for( i = 0; i<sum; i++ )
{
arg[i] = (char*)fen[i];
arg[i][strlen(arg[i])] = '\0';
}
arg[i] = NULL;
for( i = 0; i< sum; i++ )
{
if( strcmp(arg[i], "&") ==0 )
{
if( i ==sum-1 )
{
hou = 1;
arg[i] == NULL;
}
else
{
printf("輸入錯誤,後臺符必須放到最後!\n");
exit(-1);
}
}
}
//判斷有沒有輸出重定向符號'>'並且必須讓符號的後面必須要有文件名
for( i = 0; arg[i]!=NULL; i++ )
{
if( strcmp( arg[i], ">" ) == 0 && arg[i+1] != NULL)
{
flag +=1;
if( flag ==1 )
{
t =O;
file = arg[i+1];
arg[i] =NULL;
}
puts(file);
if( flag>1 )
{
printf("輸入錯誤\n");
return ;
}
}
//判斷有沒有輸入重定向'<'並且必須讓這個符號前面有東西
if(arg[i] != NULL && strcmp( arg[i], "<" ) == 0 && i!=0)
{
flag +=1;
if( flag ==1 )
{
t =I;
file = arg[i+1];
arg[i] =NULL;
}
else
{
printf("輸入錯誤\n");
return ;
}
}
//命令只有一個管道'|' 並且它的前後必須都要有東西,還要將它後面的命令記錄下來
if ( arg[i] != NULL && strcmp( arg[i], "|" ) == 0 && i!=0 && arg[i+1] != NULL )
{
arg[i] = NULL;
flag +=1;
if(flag ==1)
{
t = P;
int j;
for( j = i+1; arg[j] != NULL; j++ )
{
argnext[j-i-1] = arg[j];
}
argnext[j-i-1] = arg[j];
break;
}
else
{
printf("輸入錯誤!\n");
return;
}
}
}
if( ( pid = fork() ) < 0 )
{
printf("fork error\n");
return;
}
switch(t)
{
case 0:
if(pid == 0)
{
if(!( find_mingli(arg)))
{
printf("系統目錄下沒有%s這個命令\n",arg[0]);
exit(0);
}
if( execvp(arg[0],arg) ==-1)
{
perror("execvp");
}
exit(0);
}
break;
case 1://輸出流重定向
if( pid == 0 )
{
if( !( find_mingli(arg) ) )
{
printf("沒有這個%s命令\n", arg[0]);
exit(0);
}
fd = open(file, O_RDWR|O_CREAT|O_TRUNC, 0644);
if( fd < 0 )
{
my_error("open", __LINE__);
}
dup2( fd, 1 );
execvp(arg[0], arg);
exit(0);
}
break;
case 2://輸入流重定向
if( pid == 0 )
{
if( !find_mingli( arg ) )
{
printf("沒有這個命令%s\n", arg[0]);
exit(0);
}
if( (fd = open( file, O_RDONLY )) < 0 )
{
my_error("open", __LINE__);
}
dup2( fd, 0 );
execvp(arg[0], arg);
exit(0);
}
break;
case 3://在子進程中先執行管道符前面的,在父進程中執行後面的,父進程等待子進程結束
if( pid == 0 )
{
if( (pid2 = fork()) <0 )
{
printf("make process erron\n");
exit(-1);
}
if( pid2==0 )//在子進程中先執行管道符前面的
{
if( !find_mingli(arg) )
{
printf("沒有這個命令%s\n",arg[0]);
exit(-1);
}
if( (fd2 = open("/tmp/xiaoming", O_RDWR|O_CREAT|O_TRUNC, 0644)) <0 )
{
my_error("open", __LINE__);
exit(-1);
}
dup2( fd2, 1 );//將父進程文件的文件描述符複製給新的FD2,這樣fd2中就包含了父進程的所有信息
execvp( arg[0], arg );//在環境變量中查找可執行的程序,執行參數arg中的命令
exit(0);//關閉進程,他有一個返回值,在8到15之間,這個值放到內核中等待父進程通過wait(&reslt)來取這個值
}
if( waitpid(pid2, &ccc, 0 ) <0 )//父進程等待子進程結束
{
printf("等待失敗!\n");
}
if( !find_mingli(argnext) )
{
printf("沒有這個命令%s\n", argnext[0]);
exit(-1);
}
fd2 = open("/tmp/xiaoming", O_RDONLY);//打開子進程的那個文件,輸入到當前的程序中,執行新的參數
dup2(fd2, 0);
execvp(argnext[0], argnext);
if( remove( "/tmp/xiaoming" ) )//正確移除返回0, 沒有移除返回-1
{
printf("remove error\n");
}
exit(0);
}
break;
default :
break;
}
if( hou == 1 )
{
return ;
}
if( waitpid( pid, &status, 0) == -1 )
{
printf(" 等待錯誤 !\n");
}
}//這個函數寫完,有參數 t , arg , file ,
int find_mingli(char *arg[256])//這個函數的作用是判斷你輸入的命令在系統的命令目錄裏只否有,有則返回1,無則返回0
{
DIR *dir;
struct dirent *ptr;
int i=0;
char *path[] = {"./", "/bin", "/usr/bin", NULL};
//是當前目錄下的程序可以正常運行
if( strncmp( arg[0], "./", 2 ) == 0 )
{
arg[0] = arg[0] +2;//暫時不知道在什麼情況下用
}
while(path[i]!= NULL)
{
if( ( dir = opendir(path[i]) ) <0 )
{
printf("沒有這個參數 /bin\n");
}
while( (ptr = readdir(dir) ) != NULL )
{
if( strcmp( ptr->d_name , arg[0]) == 0 )
{
closedir( dir);
return 1;
}
}
closedir(dir);
i++;
}
return 0;
}