

1. 설명에 들어가기 전 후기
2달동안 했던 미니쉘이 드디어 끝이 났다
미니쉘을 하면서 bash에 값을 정말 많이 입력을 해봤고, 설마 이것도 돼?? 라는 말만 정말 많이 한 것 같다
파싱 파트를 맡으면서 구조를 어떻게 짜야하는지가 정말 오래걸렸고, 구현을 하는데는 생각보다 오래 걸리지는 않았다
하지만 "문자열"문자열 같은 케이스를 만나면서 기존에 구현했던 방식으로는 이 케이스를 해결할 수 가 없다는 것을 알았고, 결국 갈아엎게 되었다
갈아엎고 새로운 방법을 채택했을 때는 다행히 다양한 케이스에도 유연하게 대처할 수 있었다
다만, 문자열 내에 환경변수가 섞여 들어가는 케이스에서는 새로운 연결리스트를 하나 더 만들어야 했기에 복잡하게 작동하긴 하였다
케이스마다 leak이 나오는 것이 달랐기에, leak을 잡는 것도 한참 걸렸다
하지만 웬만한 어이없는 케이스에서도 올바르게 작동하는 것을 보면서 시간과 노력이 든 만큼 뿌듯했던 과제였다
2. 프로젝트 소개
간단한 쉘 만들기 (bash 참고)
다음의 내장 기능을 구현하기
- -n 옵션을 사용할 수 있는 echo
- 상대 또는 절대경로만 사용하는 cd
- 옵션이 없는 pwd
- 옵션이 없는 export
- 옵션이 없는 unset
- 옵션이나 인자값이 없는 env
- 옵션이 없는 exit
3. 쉘(shell)이란?
커널과 사용자간의 다리 역할을 하는 것으로, 사용자로부터 명령을 받아 해석하고 프로그램을 실행하는 역할
쉘의 기능
- 명령어 해석기 기능
- 프로그래밍 기능
- 사용자 환경 설정의 기능
4. 미니쉘의 명령어 및 단축키
명령어
- echo : 받은 인자들을 출력
- -n 옵션 : 줄바꿈을 출력하지 않음
- cd : 디렉터리 이동
- pwd : 현재 디렉터리 확인
- export : 환경변수 추가
- unset : 환경변수 삭제
- env : 현재 가지고 있는 환경변수들을 출력
- exit : 인자로 받은 숫자 중 0 ~ 255범위의 숫자를 반환하며 프로세스 종료
- 255이상의 수가 들어왔다면? : 255를 나눈 수를 반환
- 숫자가 아닌 수가 들어왔다면? : 에러메시지와 함께 255 반환
- 인자가 2개 이상 들어왔다면? : 에러메시지을 띄우지만, 프로세스 종료 x
단축키
- ctrl + c : 새로운 줄에 새로운 프롬프트 출력
- ctrl + d : 쉘을 종료
- ctrl + \ : 아무런 동작도 하지 않음
파이프라인 (|)
프로세서에서 성능을 높이기 위해, 명령어 처리 과정으로 명령어 처리를 여러 단계로 나누어 단계별로 동시에 수행하여 병령화 시키는 것
즉, 파이프라인마다 명령어의 출력값은 다음 명령어의 입력값으로 들어가야함
리다이렉션
- < : 입력 전환. 파일로 입력할 수 있음
- > : 출력 전환. 화면에 출력되지 않고 파일 등에 저장
- << : 특수 입력. 제시된 문자가 입력될 때까지 대기하다가, 제시된 문자가 입력되면 현재까지 입력한 모든 문자들을 출력
- >> : 특수 출력. 출력전환과 같이 화면에 출력된 내용을 저장하지만, 원래 내용에 추가하여 저장됨
5. lexcial analysis & syntax analysis
파싱(parsing) 이란?
일련의 문자열을 의미잇는 토큰(token)으로 분해하고, 이들로 이루어진 파스 트리(parse tree)로 만드는 과정
컴파일러에서는 토큰화하는 어휘분석 단계(lexical analysis)와, 파스 트리를 만드는 구문분석 단계(sysntax analysis)를 거친다
minishell에서의 lexcial analysis
이 단계에서는 토큰화를 진행하면서 올바르게 문장이 들어왔는지 확인했다
ex) "문자열 -> X, "문자열' -> X
문장의 끝, 리다리렉션, 파이프, 빈칸을 기준으로 문장을 나누었다
기존에는 큰따옴표, 작은따옴표를 기준으로도 나누었지만, 해당 케이스를 처리하지 못하여 갈아엎게 되었다
ex) "문자열"문자열 -> O, 문자열"문자열"문자열 -> O
문자를 다 나눈 뒤 타입을 나누었다
ex) ls -a | a < b
- ls : WORD
- -a : WORD
- | : PIPE
- a : WORD
- < : REDIECT
- b : WORD
minishell에서의 syantax analysis
이 단계에서는 토큰화되어 만들어진 연결리스트를 트리로 바꾸었다
트리를 생성할 때, 다음과 같이 우선순위를 두고 재귀를 돌았다
- 파이프
- 리다이렉션
- workd
맨 뒤에서부터 파이프가 있는 지 없는지 확인하였다
만일 파이프가 있으면 파이프를 root로 두고 파이프의 왼쪽, 오른쪽에서 재귀를 돌며 트리를 완성시켜 나갔다
만일 파이프가 없으면 앞에서부터 리다이렉션이 있는지 확인하였다
리다이렉션이 있다면 리다이렉션을 root로 두고 리다이렉션의 왼쪽, 오른쪽에서 재귀를 돌며 트리를 완성시켜 나갔다
만일 리다이렉션이 없으면 word이므로 트리의 왼쪽에다가 값을 넣었다
명령어를 가장 위에 위치시키기 위해 왼쪽에다가만 값을 넣고, 오른쪽에는 넣지 않았다
ex) ls -a | a < b

6. 파싱 파트 순서
- 입력받은 문장을 문장의 끝, 파이프, 리다이렉션, 빈칸을 기준으로 split한다
- 이때 따옴표가 잘 닫혀있는지 확인한다
- split된 단어들을 타입을 지정해주어 연결리스트를 생성한다
- &&와 ||은 오류로 해석했으므로 &&와 ||이 들어왔는지 검사한다
- 생성된 연결리스트를 트리로 만든다
- 생성된 트리를 돌며, 문법이 맞는지 검사한다
- 만들어진 트리의 root값을 구현부에 넘긴다
7. 힘들었던 케이스
구현하기 힘들었던 케이스는 다음과 같다
echo "$USER"
-> juelee
echo '$USER'
-> $USER
echo "aa$USER"
-> aajuelee
echo 'aa$USER'
-> aa$USER
echo 'aa"$USER"'
-> aa"$USER"
ec"ho" hello
-> hello
등등이 있다....
'42 Seoul' 카테고리의 다른 글
[42Seoul] CPP Module 00 (0) | 2024.03.08 |
---|---|
[42Seoul] Philosophers (0) | 2023.05.04 |
[42 Seoul] push swap (0) | 2023.02.24 |
[42 Seoul] minitalk (0) | 2023.01.05 |
[42 Seoul] so_long (0) | 2022.11.17 |