KAU/Linux

Shell Coading.

Angler.lee 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);
}