ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Shell Coading.
    KAU/Linux 2012. 5. 17. 11:03

    간단한 쉘 동작입니다.

     

    #include <stdio.h> 
    #include <string.h> 
    #include <stdlib.h> //exit 함수 사용하려면 추 
    #include <sys/types.h> 
    #include <unistd.h> 
    #include <fcntl.h> 
    
    #define MAXARG 30  //command를 저장하기 위한 
    Structure struct COMMAND
    {
    	int argv; //argument의 개수 	
    	char* arg[MAXARG]; //command 내용 	
    	char flag; //구분자(;, >, <, |, &) 
    };
    
    main()
    {
    	int i, j;
    	char buf[256];
    	char* arg[MAXARG];
    	char* s;
    	char* save;
    	int argv;
    	static const char delim[] = " \t\n"; // 공백, 탭, 개행으로 구분 	
    	int pid, status;
    	int cmdIdx; //명령어 개수 	
    	int syntax; //syntax error 판단 	
    	struct COMMAND cmd[MAXARG]; //command 저장  	
    
    	//리다이렉션에서 쓰이는 변수 	
    	int fd2, c;
    
    	//파이프에서 쓰이는 변수 	
    	int fd[2];
    	int bpipe = 0;
    	int Flag = 0;
    
    	//리다이렉션이나 파이프 실행 시 1  	
    	while (1) { // 무한 반복 		
    		printf("[shell]$ ");    // 프롬프트 출력 		
    		fgets(buf, 256, stdin); // 오버플로우 안정을 위해 
    		fgets 		argv = 0;
    		cmdIdx = 0;
    		syntax = 0;  		//초기화 		
    		for (i = 0; i < MAXARG; i++) {
    			cmd[i].argv = 0;
    			cmd[i].flag = 0;
    			for (j = 0; j < MAXARG; j++) {
    				cmd[i].arg[j] = NULL;
    			}
    		}
    		s = strtok_r(buf, delim, &save);  // 문자열에서 구분자 기준으로 단어 분리 		
    		while (argv < MAXARG && s) {
    			arg[argv++] = s;
    			s = strtok_r(NULL, delim, &save);
    		}
    
    		arg[argv] = (char*)0; // 인수가 더 없음 		
    		if (!strcmp(arg[0], "quit"))  // 단어가 ‘quit’이면 while 문 벗어남 			
    			break;  		//|, >, <, &, ; 로 구분하여 flag로 지정 		
    
    		for (i = 0; i < argv; i++) {
    			if (!strcmp(arg[i], "|"))
    				cmd[cmdIdx++].flag = '|';
    			else if (!strcmp(arg[i], ">"))
    				cmd[cmdIdx++].flag = '>';
    			else if (!strcmp(arg[i], "<"))
    				cmd[cmdIdx++].flag = '<';
    			else if (!strcmp(arg[i], "&")) {
    				if (i != argv - 1) {
    					syntax = -1;
    					break;
    				}
    				cmd[cmdIdx++].flag = '&';
    			}
    			else {
    				int lw = strlen(arg[i]) - 1; //LAST WORD 				
    				if (arg[i][lw] == ';') {
    					cmd[cmdIdx].flag = ';';
    					arg[i][lw] = '\0';
    					cmd[cmdIdx].arg[cmd[cmdIdx].argv++] = arg[i];
    					cmdIdx++;
    				}
    				else {
    					cmd[cmdIdx].arg[cmd[cmdIdx].argv++] = arg[i]; 					//마지막명령일 경우 					if(i == argv-1) cmdIdx++; 				
    				}
    			}
    		}
    
    		//syntax error 판단 		
    		if (syntax == -1 || !strcmp(arg[argv - 1], "|") || !strcmp(arg[argv - 1], ">") || !strcmp(arg[argv - 1], "<")) {
    			printf("문법 에러 입니다!!\n");
    			continue;
    		}  		for (i = 0; i < cmdIdx; i++) { 			//제일 마지막 앞을 제외하고 '>' '<'가 있으면 문법오류 			
    			if (i != cmdIdx - 2 && cmd[i].flag == '>' || cmd[i].flag == '<') {
    				printf("문법 에러 입니다!!\n");
    				continue;
    			}
    		}  		/////////////////// fork 호출에 실패하면 		
    		if ((pid = fork()) == -1)
    			perror("fork failed");
    		else if (pid != 0) {
    			//백그라운드 실행이 아니면 			
    			if (cmd[argv - 1].flag != '&')
    				pid = wait(&status); // 자식 프로세스가 끝나기를 기다림.  	 		
    				////////////////////////////////// 자식:  execvp를 이용하여 arg[0] 실행 		
    		}
    		else {
    			// Foreground, Background 프로세스 그룹 지정 			
    			for (i = 0; i < cmdIdx; i++) {
    				// |, >, < 일 경우, Falg 1로 설정 				
    				if (cmd[i].flag == '|' || cmd[i].flag == '>' || cmd[i].flag == '<')
    					Flag = 1;
    				else
    					Flag = 0;
    				if (cmd[cmdIdx - 1].flag == '&') {
    					if (Flag == 0) {
    						setpgid(getpid(), 0);
    						printf("Background 프로세스 그룹ID: %d \t\n", getpgrp());
    					}
    				}
    				else {
    					if (Flag == 0)
    						printf("Foreground 프로세스 그룹 ID: %d\t\n", getpgrp());
    				}  				// 리다이렉션 기능 				
    
    				if (cmd[i].flag == '>') {
    					fd2 = creat(cmd[i + 1].arg[0], 0600);
    					dup2(fd2, STDOUT_FILENO);
    					execvp(cmd[i].arg[0], cmd[i].arg);
    				}
    				else if (cmd[i].flag == '<') {
    					fd2 = open(cmd[i + 1].arg[0], O_RDONLY | O_CREAT, 0600);
    					dup2(fd2, STDIN_FILENO);
    					execvp(cmd[i].arg[0], cmd[i].arg);
    				} 				// 파이프 기능
    				else if (cmd[i].flag == '|') {
    					//처음파이프 					
    					if (bpipe == 0)	pipe(fd);
    
    					if (bpipe == 0 && fork() == 0) {
    						close(fd[0]);
    						dup2(fd[1], STDOUT_FILENO);
    						close(fd[1]);
    						execvp(cmd[i].arg[0], cmd[i].arg);
    						perror("Connect");
    						exit(0);
    					}
    					else { 						//자식이 실행한걸 읽어오세요. 
    						close(fd[1]);
    						dup2(fd[0], STDIN_FILENO);
    						close(fd[0]); 						//다음번에도 파이프 플래그가 있을 경우 
    						if (cmd[i + 1].flag == '|') {
    							//새로운 파이프를 생성하여
    							pipe(fd);
    							//자식은 파이프에 다시 실행한 값을 쓰도록 하고 종료
    							if (fork() == 0) {
    								close(fd[0]);
    								dup2(fd[1], STDOUT_FILENO);
    								close(fd[1]);
    								execvp(cmd[i + 1].arg[0], cmd[i + 1].arg);
    								perror("Connect");
    								exit(0);
    							}
    							//부모의 경우에는 다음 루프에서 이 루트로 진행 할 수 있도록  플래그 pipe를 1 
    							else
    								bpipe = 1;
    							//인다이렉션 플래그가 있을 경우 실행하지 않음 						
    						}
    						else if (cmd[i + 1].flag == '>' || cmd[i + 1].flag == '<') {
    							bpipe = 0;
    							//그외 플래그 일경우 그냥 실행
    						}
    						else {
    							execvp(cmd[i + 1].arg[0], cmd[i + 1].arg);
    							perror("Connect");
    							bpipe = 0;
    						}
    					}
    				}
    				else if (cmd[i].flag == ';') {
    					//자식은 실행하고
    					if (fork() == 0) {
    						execvp(cmd[i].arg[0], cmd[i].arg);
    					} 					//부모는 기다리고
    					else {
    						pid = wait(&status); // 자식 프로세스가 끝나기를 기다림.
    					}
    				}
    				// 그 외의 명령어 실행 				
    				else {
    					if (Flag == 0)
    						execvp(cmd[i].arg[0], cmd[i].arg);
    					else
    						exit(0);
    				}
    			}
    		}
    	}
    	exit(0);
    }

    댓글

Designed by Tistory.