본문 바로가기

42cursus

minishell - 간단한 정리

0. 프로젝트 목표

- bash와 기능이 유사한 minishell 만들기

- 문자열 분석, 프로세스, 파일 디스크립터 관리가 주를 이룸

- 즉, 파싱, 포크, 파이프에 대해 중점적으로 학습하고 작성해야 함

- 또한 구조적으로 잘 잡아야지 클린한 데이터를 계속들고 다니면서 코딩할 수 있음

 

 

 

1. 학습

우선 키워드 정리하고 함수에 대한 정리가 필요

malbongcode.tistory.com/158

 

minishell - 명령어와 외부함수 정리하기 (command & External functions, 42seoul)

명령어 정리 echo - 출력 관리 cd, pwd - 디렉터리 관리 export, unset, env - 환경변수 관리 - env와 export차이가 하나씩 존재했음 -> _=/usr/bin/env(env) , OLDPWD(export) exit - 프로세스 종료 세미콜론(;)..

malbongcode.tistory.com

 

 

 

2. 구조잡기

큰 틀

1. 데이터 초기화 (환경변수, 들고다닐 데이터변수)

2. 프롬프트 찍기

3. while문 실행

  -> 문자열 읽기

  -> 파싱하여 토큰화 하기

  -> 파싱한 데이터 검사하기

  -> 토큰화한 데이터로 실행시키기

 

파싱단계

%%% 따옴표는 현재 line에 i번째가 쌍따옴표 안인지 밖인지, 홑따옴표 안인지 밖인지에 대한 함수를 만들어서 편했음 %%%

1. EOF아닐 때 까지 한 줄 읽기

2. 따옴표안쪽이 아닌 스페이스로 구분하기

3. 쌍따옴표 안에서 이스케이프로 작동한 \ 제거하기

4. 이스케이프된 문자는 음수로 바꿔놓기 (음수는 ?로 표시함)

5. $는 이러한 과정을 한번 더함, 홑따옴표에서 $가 쓰이면 이스케이프된 문자로 생각해서 음수로 바꿔놓기

-> echo "Hello     ? ? $A" $A '?A\\\' >> tmp1 > tmp2 ; echo asdf | grep d

6. 이러한 것을 스페이스 단위로 토큰화 하기

7. 위치를 기반으로 토큰의 타입정하기 (CMD, ARG, OUTPUT, INPUT, PIPE....)

 

실행단계

%%% CMD토큰타입만 찾아서 실행함, 또한 CMD단위의 구분은 |나 ;나 끝이 구분함 %%%

1. 현재 CMD가 가지는 블록의 토큰들을 깨끗하게 바꿔줌

 -> $VAL을 환경변수에서 찾아 문자열로 치환해서 넣어줌

 -> 따옴표 제거

 -> 음수로 처리했던 이스케이프 문자를 다시 양수로 토글함

2. 현재 CMD블록의 이전 파이프가 있으면 dup2함 -> 하자마자 일반 fd를 close함

3. 현재 CMD블록의 이후 파이프가 있으면 dup2함 -> 하자마자 일반 fd를 close함

4. 현재 CMD블록 근처의 redirection을 처리함 -> open -> dup2 -> close

 -> 2,3,4단계로 STDOUT, STDIN의 가르키는 파일이 바뀌었을 것

5. cmd를 만들어줌 -> CMD하나와 ARGS들을 char **로 만들어줌

6. exec함 (execve할 때는 fork해서 처리함)

 

 

 

3. 나의 minishell 구현정도

대부분 bash와 동일, 다른 부분은 아래와 같음

더보기

역슬래쉬는 쌍따옴표 안에만 작동, 밖에 있으면 에러 처리 

-> ex) echo \""" X

-> ex) echo "\"" O

 

어느 한 블록에 cmd가 없으면 에러처리함

-> ex) > file.txt

 

시그널에서 입력중일 때 +  Ctrl + \  + 이후에 입력하면 이후에 것만 남아있음에 유의

-> ex) echo^\ ls -> ls 실행

 

$? (exit_code)

-> a$? | b$? | c$? 같은 경우 구현하지 않음

-> 0 1 127 126 258 130 131 정도만 구현

0 : success

1 : file not found

127 : cmd not found

126 : is dir

258 : syntax error

130 : child int

131 : child quit

 

~ 구현하지 않음 (~은 순수한 절대경로, 상대경로가 아님)

 

builtin과 실행파일은 다름에 주의

 -> unset PATH를 했다하더라도 echo 같은 명령어를 builtin으로 구성했기 때문에 실행이 되어야 함

 

 

 

4. 힘들거나 어려웠던 점

1. 프로세스와 파일디스크립터에 대한 이해가 부족하여 서브젝트에 대한 감도 못잡음

 

2. 이해했으나 어디에 사용하는지 모름

 

3. 문자열 분석, 파싱부터 하려하는데, 쌍따옴표, 홑따옴표, 이스케이프, $에 대한 예외가 너무 많았음

 

4. 분업의 어려움 -> "난 파싱할테니 넌 실행해" 이런식의 분담을 하려면 옆에 딱 붙어 있어야 할 듯

 

5. bash와 유사도를 높이려다 기존의 코드가 망가지는 일이 발생하여 복잡해짐

 

6. 구현에 힘쓰다가 내 코드를 동료분이 읽기가 힘들어짐 (놈과 관련이 크긴 함)

 

7. 몸이 아프면 그냥 힘듦

 

8. fork()를 언제 해야할까, close()를 언제 해야할까, pipe()를 언제 해야할까에 대한 의문

 -> 이 구조를 짜는게 가장 어려웠는데

 -> 간단한 test_main.c을 만들어서 하나씩 해봄

 -> fork는 exec하기전에 하면 됨

 -> close는 dup2하면 close당기면 됨

 -> pipe는 CMD블록에서 다음파이프토큰이 있으면 pipe 생성 해놓으면 됨