- 여러가지 명령어를 한줄에 적을수 있어야 한다
- 오리지널 쉘은 세미콜론(;)을 통해서 여러개의 명령어를 실행할 수 있다
- 유저들이 한 줄에 여러개의 명령어를 실행 할 수 있도록 개선해보자- 백그라운드 처리
- 오리지널 쉘은 사용자가 프로세스를 백그라운드에서 실행할 수 있다
- 사용자가 명령어를 '&'로 끝내면, 프로세스를 백그라운드에서 실행할 수 있다
- 백그라운드 실행이란? 다른 명령어를 사용하면서 해당 프로세스는 계속해서 실행되는 상태임을 의미
- 다른 작업을 수행하면서 백그라운드에서 실행중인 프로세스를 동시에 관리 가능- exit 명령어
- exit명령어를 통해 쉘을 종료시킬수 있는 기능을 추가해야한다!
이렇게, 여러가지 개선점이 있지만, 우선 if..then 즉 control flow를 적용시켜보자
if date | grep Fri
then
echo time for backup. Insert tape and press enter
read x
tar cvf /dev/tape /home
fi
여기서 grep 프로그램이 "Fri"라는 단어를 찾았을때는 성공했음을 나타내기 위해 exit(0)을 호출한다
- 0이라는 종료값은 성공을 나타냄!
스크립트에는 else블록을 추가할 수 있다. 이는 then블록과 유사하다
- 문법을 확인해보자!
사실 예시만 봐서는 영 알아보기 힘들다. if가 어떻게 작동하는지 순차적으로 알아보자
자! 이제 새로운 쉘을 다시 만들어보자!
이렇게! 분리된다는 뜻
이제 본격적으로 코드를 살펴보자!
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "smsh.h"
#define DFL_PROMPT "> "
int main(){
char *cmdline, *prompt, **arglist;
int result, process(char**);
void setup();
prompt = DFL_PROMPT;
setup();
while((cmdline = next_cmd(prompt, stdin)) != NULL){
if((arglist = splitline(cmdline)) != NULL){
result = process(arglist);
freelist(arglist);
}
free(cmdline);
}
return 0;
}
void setup(){ //setup함수가 정의됐다! 살펴보자
signal(SIGINT, SIG_IGN); //SIGINT는 인터럽트 신호, Ctrl+C를 입력하면 발생
//SIG_IGN을 사용해서 시그널을 무시하도록 설정
signal(SIGQUIT, SIG_IGN); //SIGQUIT은 종료신호, Ctrl+\를 입력하면 발생
//SIG_IGN을 사용해서 시그널을 무시하도록 설정
}
void fatal(char *s1, char *s2, int n){ //fatal함수 정의
fprintf(stderr, "Error: %s, %s\n", s1, s2);
// fprintf함수를 사용하여 에러 메세지를 출력, stderr은 에러 출력 스트림, s1값과 s2값을 출력
exit(n); //exit함수를 호출하여 프로그램 종료, n은 종료상태 코드, 프로그램의 종료상태를 나타냄
}
자! 이게 바로 바뀐 smsh2.c 코드이다!
main 함수 부분만 smsh1.c 에서 바뀌었다 한번 살펴보자
int main(){
char *cmdline, *prompt, **arglist; //세개의 포인터 변수 선언
int result; //int형 변수 선언
process(char**) //process라는 char**형 인자를 받는 함수 프로토타입 선언
void setup(); //setup함수의 프로토타입 선언
prompt = DFL_PROMPT; //prompt라는 char형 포인터 변수에 매크로 정의된 DFL_PROMPT 대입
setup(); //setup함수 호출
while((cmdline = next_cmd(prompt, stdin)) != NULL){
// next_cmd함수를 호출해서 prompt와 stdin에서 커맨드 라인을 읽어온다
// 읽어온 결과를 cmdline 변수에 대입하고, cmdline이 NULL이 아닌동안에 반복
if((arglist = splitline(cmdline)) != NULL){
// splitline 함수를 호출, cmdline을 분할해서 arglist에 저장
// arglist가 NULL이 아닌 경우 실행됨
result = process(arglist); process 함수의 결과값을 result에 대입한다
freelist(arglist); //arglist의 메모리 해제
}
free(cmdline); //cmdline의 메모리 해제
}
return 0; //반환값을 0으로 설정, 프로그램이 종료됨
}
그래서 그놈의 process함수는 어딨나요!
다음 코드에 있습니다!
next_cmd함수는 어딨나요?
마찬가지로 다음 코드에 있습니다!
메인함수는 거의 다른 함수를 사용해서 기능한다 어서 다른 함수들을 살펴보자!
#include <stdio.h>
#include "smsh.h"
int is_control_command(char*); //함수의 프로토타입 선언
int do_control_command(char**); //함수의 프로토타입 선언
int ok_to_execute(); //함수의 프로토타입 선언
int process(char**args){ //포인터 변수를 인자로 받는 process함수
int rv = 0; //int형 변수 rv 선언
if (args[0] == NULL){ //매개변수로 받은 배열이 비어있는 경우,
rv = 0; //rv를 0으로 초기화
}
else if (is_control_command(args[0])){ //is_control_command가 args[0]의 값을 가질때
rv = do_control_command(args); //do_control_command의 반환값을 rv에 저장한다
}
else if (ok_to_execute()){ //ok_to_execute()함수의 반환값이 입력되면
rv = execute(args); //execute함수의 반환값을 rv에 저장한다
}
return rv; //rv값 반환!
}
여기서도 마찬가지로 다양한 함수를 사용해서 rv값을 반환하고 있다!
여기서도 다양한 함수의 프로토 타입만 정의되어있다. 다음 코드에 답이 있을것이다!
다음 코드를 한번 살펴보자
#include <stdio.h>
#include <string.h>
#include "smsh.h"
enum states {NEUTRAL, WANT_THEN, THEN_BLOCK}; //enum은 열거형 변수
enum result {SUCCESS, FAIL};
static int if_state = NEUTRAL;
static int if_result = SUCCESS;
static int last_stat = 0;
int syn_err(char*);
int ok_to_execute(){
int rv = 1; //rv 변수를 1로 초기화
if(if_state == WANT_THEN){ //if_state가 WANT_THEN인 경우
syn_err("then expected"); //then expected이라는 오류 메세지 출력
rv = 0; //rv는 0 으로 초기화
}
else if(if_state == THEN_BLOCK && if_result == SUCCESS){
// if_state가 THEN_BLOCK이고, if_result가 SUCCESS와 값이 같다면
rv = 1; //rv값에 1 대입
}
else if(if_state == THEN_BLOCK && if_result == FAIL){
// if_state가 THEN_BLOCK과 같고, if_result가 FAIL인 경우
rv = 0; //rv값을 0으로 설정
}
return rv; //rv값 반환
}
int is_control_command(char* s){
return(strcmp(s, "if")==0||strcmp(s, "then")==0||strcmp(s, "fi")==0);
// strcmp 함수를 사용해서 문자열 s와 "if", "then", "fi"와 같은지 확인한다
// 문자열이 일치하는 경우 1 반환
// 일치하지 않는 경우 제어 명령어가 아니라고 판단, 0을 반환
}
int do_control_command(char** args){
char* cmd = args[0]; //args 배열에서 명령어를 가져온다
int rv = -1; // rv를 -1로 초기화
if(strcmp(cmd, "if") == 0){ //만약 if명령어인 경우
if(if_state != NEUTRAL){ //if_state 가 NEUTRAL이 아닌경우
rv = syn_err("if unexpected"); //if unexpected를 출력한다
}
else{ //나머지 경우에는
last_stat = process(args+1); //last_stat에서는 마지막 명령어의 실행 결과 저장
if_result = (last_stat == 0 ? SUCCESS : FAIL);
//if_reault값을 설정
if_state = WANT_THEN; //if_state를 WANT_THEN으로 설정
rv = 0; //0을 반환
}
}
else if(strcmp(cmd, "then") == 0){ //then 명령어일 경우
if(if_state != WANT_THEN){ //if_state가 WANT_THEN이 아닐경우
rv = syn_err("then unexpected"); //에러 메세지를 출력
}
else{ //아닌 경우?
if_state = THEN_BLOCK; //if_state에 THEN_BLOCK을 대입
rv = 0; //0을 반환
}
}
else if(strcmp(cmd, "fi") == 0){ //fi명령어인 경우
if(if_state != THEN_BLOCK){ //if_state가 THEN_BLOCK이 아닌경우
rv = syn_err("fi unexpected"); //에러 메세지 출력
}
else{ //아닌경우?
if_state = NEUTRAL; //NEUTRAL을 설저
rv = 0;
}
}
else{ //그 외의 경우
fatal("internal error processing:", cmd, 2); //내부 오류를 알림
}
return rv; //2를 반환한다
}
int syn_err(char* msg){ //이건 단순히 에러메세지 출력하는 함수!
if_state = NEUTRAL;
fprintf(stderr, "syntax error: %s\n", msg);
return -1;
}
하 길고도 길었다 if, then, fi를 입력받았을때의 처리를 나타내는 코드이다!
이제 어서 결과가 어떻게 실행되는지 확인해보자
정상적으로 if, then, fi가 실행됨은 볼 수 있다
자, 오늘은 이렇게 if, then, fi를 처리하는 코드를 작성해보았다! 다음에는 더 발전된 쉘을 만들어보자