[42Seoul] - minishell

Joey·2022년 9월 26일
1

42 SEOUL

목록 보기
6/20



1.무엇을 하는 과제인가?

: bash나 zsh등과 같은 shell을 만드는 것이다.(*shell : kernel과 사용자간의 다리 역할을 한다고 생각하면 된다.)
: bash를 기반으로 shell을 만든다.

: 보통 minishell 프로젝트를 진행하면, 파싱과 실행부분 두단계로 나눠서 팀원과 역할을 분배를 한다.
둘다를 전부 각각 해보는 팀이 있고, 나눠서 하는 팀이 있는데 우리의 경우 내가 실행부분, 팀원이 파싱부분을 담당하였다.
파싱이 상대적으로 더 피곤하고 해야될 일이 많을 수 있다. 나는 실행부분도 나중에 되니 예외처리를 너무 많이 해야 되서 힘들었지만 그래도 pipe의 개념이라던지 리다이렉션을 어떤 식으로 bash에서 하는지 등을 알 수 있어서 좋았다.







2.과제를 하면서 참고 했던 곳들

-.tree구조로 파싱을 하신분, flow chart가 있어서 처음 구조를 알기에 좋았다.
https://epicarts.tistory.com/163

-.간단하게 요약하신분
https://malbongcode.tistory.com/159

-.jseo님, 항상 과제를 할때 정리를 잘하셔서 보기 좋았다.
https://bigpel66.oopy.io/library/42/inner-circle/10

-.pipex참고(이 과제를 하면서 pipex를 이전에 하지 않은 나는 pipex를 먼저 공부할 수 밖에 없었다.)
https://bigpel66.oopy.io/library/42/inner-circle/8

-.bash manual
https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Bourne-Shell-Builtins

-.bash 한글로 설명되어 있는 곳
https://wikidocs.net/29930





3.과제를 하면서 공부했던 것들

1)pipe

: 이전에 philosopher를 하면서 fork로 multi processing은 만든 적이 있으나 부모와 자식 프로세스간에 통신을 signal로만 처리를 해주었다. 그래서 pipe 자체가 굉장히 생소하였고, 프로젝트를 진행하면서 이 부분부터 공부를 해야됐다.(원래는 pipex라는 과제에서 진행하지만 나는 다른 과제를 하였기 때문에 이를 다시 공부해야만 했다.)

: 멀티프로세싱에서 각 프로세스간에 통신을 할 수 있는 도구라고 한줄로 설명을 할 수 있겠다. 그게 자식과 부모이든 자식과 자식이든 여러 상황이 존재할 것 같다.

: 파이프의 통신은 단방향 통신이다. 무전기라고 생각하면 편하겠다. 내가 말하면 말만할 수 있고 소리는 들을수 없다. 마찬가지로 파이프는 한쪽에서 쓰고 한쪽에서는 읽기만하는 단방향 통신이라고 알고 있으면 된다. 여기까지는 간단한데 리다이렉션이 들어가면 복잡해 지게 된다. 사용자가 파이프만 쓸리는 없고 리다이렉션도 같이 쓰기 때문이다.

: pipe를 실행하면 fd값을 두개를 얻게 된다. 하나는 입력용, 하나는 출력용인데 각 프로세스에서는 한쪽을 close(fd)로 닫아주고 다른 한쪽만 사용하게 된다. 또한 fd의 stdout(1, 출력)이나 stdin(0, 입력)을 통하여 사용을 하고 싶으면 dup2함수를 이용하여 현재의 fd를 다른쪽에 복사하는 식으로 사용을 하였다.(쓰려고 하는 입력용 fd가 5였다면 5를 0으로 dup2함수를 통해 실행하고 5는 close해준다)

2)리다이렉션

: 입력(<), 출력(>), 추가입력(>>), heredoc(<<)의 상황을 말한다. 예를 들어 cat < a라고 한다면 a의 파일을 읽어서 입력을 하라는 뜻과 같다.

: 다시 돌아와서 파이프의 이야기를 하자면 이 리다이렉션의 입력은 보통 fd(File Descriptor) 0을, 출력은 1을 써서 하게 된다. 기존에 우리가 printf를 쓰면 fd는 기본적으로 1로 설정하여 출력을 한다. 그런데 파이프나 리다이렉션이 많아지면 우리는 그에 맞게 이 값들을 적절하게 조절을 해주어야 한다. 무엇을 입력하고 무엇을 출력할지를 말이다.

3)실행순서에 따른 프로그램 동작

: 실행단계의 핵심이 이 부분이다. 무엇을 먼저하고 무엇을 나중에 할지 결정을 해준다.
이 부분이 틀어지면 원하는 대로 출력이 안되고 꼬이게 된다.

나의 경우는 아래와 같이 동작을 시켰다.


1.리다이렉션
2.파이프가 있다면 파이프 처리(여기에는 현재의 출력을 파이프에 넣을지의 여부도 들어있다)
3.멀티프로세싱 처리(fork)
4.실행(이곳에서는 built-in함수를 실행할지 execve함수를 이용해서 함수를 호출할지)
: 이 단계에서 원래는 pipe단위로 실행을 하고 wait(자식프로세스를 기다리는 것)을 하였지만, 추후에 signal동작(ctrl+c 등)을 하기 위해서 pipe단위가 아니라 전부 다 실행시키고 wait을 해주는 단계로 바꿔주었다.
5.exit처리(fork가 있다면 built_in함수들은 exit처리를 따로 해주어야 되고 execve는 실행되면 그 자체로 그 프로세스는 종료가 된다)
6.shell의 재입력 단계(메모리의 적절한 해제 등)


4)bash의 여러 동작들

: 분명 과제를 시작할때는 minishell이라고 했는데 과제가 끝날때쯤 되면 bigshell까진 아니더라도 middle shell정도는 되는 것 같다... 평가자분들한테 무수한 예외상황을 배우게 된다. 기억나는 대상들을 아래에 적어본다.

-.shell Level
: 환경변수를 보면 shell level이 존재한다. 예를 들면 minishell을 들어가고 또 minishell을 들어가면 shell level이 증가하게 된다. 끝나기 전쯤에 우리는 이것을 바꿔주었다.

-.redirection
: ls -al > a < b | grep a을 하면 어떤 값이 나올 것 같은가? ls -al에 대한 사항은 a에 저장이 되며 빈 값을 pipe로 넘겨주어 grep a는 아무것도 뜨지가 않는다. 혹시나 파일 b가 없다면 에러를 띄우게 된다.
: cat << a << b << c | cat << d << e 이런 대상들도 heredoc을 전부 다 잘 처리를 해주어야 한다. 나는 heredoc을 임시파일에 저장을 해주고 나중에 입력해주면 삭제를 하는 식으로 heredoc을 처리해주었다.

-.cat|cat|cat|cat|cat|cat|cat|cat|cat|cat|cat|cat|cat|cat
: 이런 대상들은 결국 건내주고 건내주어 cat과 같은 동작을 하게 된다.

built함수들====================================
-.cd(폴더 이동)
: 제일 힘들었던 built_in함수 처리였다. 적정선이 어딘 까지 잘 모르겠다
: cd - 이전폴더로 이동
: cd 나 cd ~ 은 홈폴더로 이전을 한다 (/User/xxxxx)
: cd / 는 루트폴더로 이전을 한다 (/)
: cd .은 현재 폴더, cd..은 이전폴더, cd...은 에러를 내야 한다.
: cd에도 폴더의 권한처리에 대한 에러처리를 해줘야한다(Permission denied)
: 환경변수의 pwd를 지우면 에러처리가 날 수도 있는데 이를 조심해야한다.(cd를 pwd기반으로 만들었기 때문에 이러한 문제가 발생한다. unset으로 pwd를 지운다면 현재 폴더를 다른 곳에 기억하게 해주는 것이 좋다.)

-.export(환경 변수 셋팅)
: export도 나중에 머리를 아프게 한 대상중 하나이다. export에는 변수명의 규칙이 있다. 첫번째 글자는 소문자,대문자,_(under bar)만 가능하다. 이를 주의해야한다.
: 여러개를 쓰면 여러개가 다 들어가야 한다.
: =를 기점으로 아무것도 안쓰이더라도 값은 들어가야 한다(""로 넣어주었다.)
: env출력시에는 value가 없는 대상은 출력이 안된다, export만 치면 value없는 대상도 출력이 되어야 한다.
: =다음에 =이 와도 value로 인식한다.(처음에 split으로 이걸 만들었지만, 나중에 고치다가 에러가 나서 평가를 망친경우도 생겼다)

-.env(환경변수 출력)
: env는 위에 말한 것처럼 value가 없는 대상은 출력하지 않는다.
: _에는 이전의 명령어나 이전 명령어에 인수가 있는 경우 인수를 저장해 놓는다
(예를 들어 ls만 쳤으면 ls가 저장되어 있고, ls -al을 쳤으면 -al이 저장되어 있다.)

-.unset(환경변수 삭제)
: unset도 뒤에 여러개가 나오면 다 처리를 해주어야 한다.
: unset도 변수명에 대한 예외처리가 존재한다.

-.echo(출력하기, n옵션)
: echo는 n옵션까지만 같이 구현을 한다. n옵션은 개행없이 출력을 하는 것이다.
: echo -n hello는 쉽다. echo -nnnnnnn hello 도 된다. echo -n -n -n -nnnnnn hello 옵션도 된다.

-.exit(종료하기)
: exit만 치면 바로 종료가 된다.
: exit 숫자를 치면 그 숫자가 종료상태에 저장이 된다.(종료상태 보는 법은 $?를 하면 된다)
: exit 숫자를 칠때 뒤의 숫자가 0~255가 아니면 overflow되서 보인다. (예를 들면 -1을 치면 뜬금없이 255가 나오지만 이는 뒤로 한칸가서 255가 된 것이다)
: 숫자의 범위는 대략 int범위까지 했다. bash는 이보다 더 넓은 것 같았다.
: 숫자의 범위를 넘어서거나 글을 치면 에러처리를 해줘야 한다.






4. 회고

1.파싱을 해보지 않아서 조금 안타깝긴 했다. 근데 특별한 것을 제외하고는 나중에는 파싱이나 실행 둘다 노가다 업무로 전락되었던 것 같다. 예외처리만 정말 2주 넘게 하였다.

2.실행 단계에서 파이프 처리나 리다이렉션 처리를 하면서 dup이나 dup2를 배우게 되어서 좋았다. pipex를 안했던 나였기 때문에 어떻게 통신을 하는지, 어떻게 자료를 넘겨주는지 등을 이곳에서 몰랐다면 아쉬웠을 것 같다.

3.마지막쯤 되어서는 너무 빨리 끝내고 싶어서였는지, 생각없이 과제를 하였던 것 같다. 그도 그럴 것이 더이상 배울 것은 없고 예외처리만 늘어가는 상황에서 과제를 빨리 끝내고 싶었다. (회사를 가게 된다고 해도 비슷한 업무의 연장이지 않을까 하는 생각도 해보았다..)

4.git을 처음으로 협력으로 사용을 해보았다. 중간쯤부터 사용을 시작했는데 그 이전까지는 서로 너무 힘들었던 것 같다. branch를 나누고 이를 적절할때 merge해서 하나로 만들어서 다시 branch를 만들고를 반복하였다. 이로 인해 빠른 프로그램 수정들을 할 수 있어서 좋았다. 다음 팀 프로젝트를 할때에도 git을 적극적으로 사용해야겠다는 생각을 해보았다.

5.어느덧 들어온지도 꽤나 시간이 흘렀고 서클상으로는 중반을 넘긴듯 하다. 물론 더 힘든 과제들이 앞에 있긴 하지만 한번쯤 점검을 하고 가야된다는 생각이 앞을 가렸다. 할 것은 태산인데, 열심히는 하고는 있지만 무언가 효율이 나고 있지 않다는 생각을 하면서 했던 것들을 복습을 잘 해 가야겠다는 생각을 해보았다. 빠름도 좋지만 제대로가 더 필요한 하루하루이다.

profile
세상을 이롭게 하는 프로그램 만들기

1개의 댓글

comment-user-thumbnail
2022년 9월 27일

잘 봤습니다!

답글 달기