-
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); }