minishell
1. 소개
As beautiful as a shell
42서울 본과정 입과 후 여덟번째로 수행한 과제로, 작은 크기의 shell을 구현하는 과제이다. 우리는 이제 간단한 빌트인 함수부터 여러 명령어를 수행할 수 있는 간단한 shell을 만들어야 한다. 동굴 벽에 흐릿하게 비치는 bash를 구현하는 것이 목표인 것이다. 너무 큰 shell을 만들기 위해서 노력하다가 블랙홀에 빠지지 않게 조심하자.
shell의 내부 구현 (fork와 execv를 활용한 명령어 처리 등) 은 이미 이전 과제인 pipex 과제에서 진행한 바 있다. 따라서 pipex의 핵심 로직을 바탕으로 각종 명령어와 파싱부분을 구현해 조합하는 프로젝트가 되겠다. 컴파일러 개론을 들었거나, 시간적 여유가 많았다면 파싱부분도 깔끔하게 트리로 할 수 있었겠지만 현실적인 문제로 도전하지 못한 것이 아쉽다. 다행히 파싱부분은 뛰어난 팀원분께서 인고의 예외처리를 통해 구현해주셨다!
- only c언어로만 개발해야 했기에 트리를 사용하고 싶다면 자료구조를 실제로 구현해야했던 것...
- 다음 과제까지 시간적 여유가 많이 남아있지 않았던 것...
- 아쉽지만 파이프 구현과 shell의 동작 방식을 최대한 구현해보려 노력했다.
2. minishell 명세서
- PROGRAM NAME
minishell
- PARAMETER
commands...
- DESCRIPTION
-
명령 대기 중 프롬프트 표시: 새 명령을 기다릴 때 프롬프트를 표시해야함!
-
작업 이력 관리: 이전 명령의 작업 이력을 관리할 수 있어야 함!
-
실행 파일 검색 및 실행:
PATH변수를 기반으로 올바른 실행 파일을 검색하고 실행해야하고, 상대 경로나 절대 경로를 사용할 수 있어야 한다. -
시그널 처리: 전역 변수 사용은 오로지 하나만 허용
-
해석하지 말아야 할 항목: 닫히지 않은 인용부호(따옴표)나 주제와 관련 없는 특수 문자(
\,;)는 해석하지 않아야 한다. -
따옴표 처리
- 싱글 쿼트 (
'): 인용된 시퀀스에서 메타문자가 해석되지 않도록 해야 함 - 더블 쿼트 (
"):$를 제외한 메타문자가 해석되지 않도록 해야 함
- 싱글 쿼트 (
-
리디렉션 구현:
<: 입력을 리디렉션>: 출력을 리디렉션<<: here document- 구분자를 지정한 다음, 구분자를 포함한 줄을 읽을 때까지 입력을 받아야 한다.
- 이 작업은 이력에 기록되지 않아야 한다.
>>: 출력을 추가 모드로 리디렉션
-
파이프라인 구현
|로 각 명령의 출력을 다음 명령의 입력으로 연결해야 함.
-
환경 변수 처리
$로 시작하는 문자열은 해당 값으로 확장되어야 함.
-
특수 변수 처리
$?: 최근에 실행된 포그라운드 파이프라인의 종료 상태로 확장되어야 함.
-
특수 키 처리
Ctrl-C: 새로운 줄에 새 프롬프트를 표시Ctrl-D: 셸을 종료Ctrl-\: 아무런 동작도 하지 않음
-
built-in 구현
echo: 옵션-n지원cd: 상대 경로 또는 절대 경로만 지원pwd: 옵션 없이 지원export: 옵션 없이 지원unset: 옵션 없이 지원env: 옵션이나 인수 없이 지원exit: 옵션 없이 지원
-
3. 개념 정리
3-1. Shell
Shell은 사용자가 OS와 상호작용할 수 있도록 해주는 프로그램이다. 사용자가 명령어를 입력하면 이를 OS가 이해할 수 있도록 해석하고, 그에 따른 작업을 수행한다. Shell은 커맨드 라인 인터페이스를 제공하며, 사용자의 명령어를 읽고-해석하고-실행하고-결과를 출력한다.
shell에는 Bash, Zsh 등등 다양한 종류가 존재한다. 이때 Bash는 가장 널리 사용되는 shell이고, 클러스터 맥에 설치되어있는 기본 shell 이기도 하다. bash의 동작을 잘 뜯어보면서 과제를 수행해보자.
3-2. 환경 변수
환경 변수는 운영 체제에서 프로그램이 실행되는 동안 사용할 수 있는 변수이다. 이 변수들은 시스템 전체에 걸쳐 정보를 전달하거나 설정을 저장하는 데 사용된다. 환경 변수는 주로 시스템 경로, 사용자 정보, 설정 정보 등을 담고 있다.
3-3. built-in 함수
과제 상 구현해야하는 built-in 함수의 정체는 무엇일까?
Shell에서 built-in 함수란 Shell 자체에 내장된 명령어 또는 기능을 의미한다. 이러한 함수는 별도의 외부 프로그램을 실행하지 않고, Shell 자체에서 직접 수행된다. 이는 특정 작업을 보다 빠르게 수행할 수 있게 하며, Shell 스크립트에서 자주 사용되는 기본적인 작업들을 처리하는 데 유용하다.
이 말은 즉슨, 해당 함수들은 환경변수 PATH가 변경되거나 망가지는 일이 있어도 정상적으로 동작해야한다는 점! 또한 Shell 자체에서 실행한다는 점을 곱씹으면, built-in 함수를 실행할 때는 fork를 사용할 필요 없다는 것도 알 수 있다.
4. Mandatory
이번 과제는 2인이 협동해야하는 팀 과제이다. 주로 역할분배는 명령어 파싱부와 실행부로 구분하곤 한다.
명령어 파싱의 경우 bonus를 고려하지 않으면 주로 연결리스트로 관리를 하고, bonus를 고려할 경우 트리형태로 관리한다. 실행부의 경우 아까 언급한 것 처럼 이전 과제인 pipex와 유사하기 때문에 익숙한 작업이 주를 이룬다.
내가 실행부 + built-in 함수를 맡았고, 함께한 팀원분이 파싱부 + here_doc 등을 맡았다.
이에 따라, 이번 과제 정리는 내가 담당한 빌트인 함수만 주로 다루어보고자 한다. (실행부는 pipex와 유사). 코드를 실제로 나열하기보다는 각 함수들이 어떠한 특징을 가지고 있는지 알아보자! 함수들의 작동은 모두 bash을 기준으로 작동을 확인해보자. 직접 디렉토리 삭제 및 복구, 이상한 값들, 기괴한 상황을 bash에서 진행해보며 맞춰보는 것이 좋다. 생각보다 man page에 모든 것이 나와있지는 않음...
4-1. built-in 구현
ft_cd
-
cdHOME환경 변수를 찾아 해당 경로로 이동HOME이 없으면 에러 반환
-
cd pathpath가 존재하는지 검사 후 이동.- 이동에 성공한 경우 환경변수
PWD와OLDPWD를 갱신 - 실패 시 에러 반환
-
cd ~HOME으로 이동- bash shell 상에서
HOME환경변수를 제거한 상태에서도 작동하는 것을 확인했기에, 이 명령 작동이 환경변수에 얽메이는 것이 아니라고 판단했음
-
cd -- 이전 디렉토리로 이동
ft_echo
-
echo- 인자가 없으면 개행(
\n) 출력
- 인자가 없으면 개행(
-
echo text- 입력된 문자열을 그대로 출력하고 개행(
\n) 추가
- 입력된 문자열을 그대로 출력하고 개행(
-
echo -n text-n옵션을 감지하면 개행 없이 출력
-
echo -n -n -n text-n이 여러 개 있어도 하나의 옵션으로 처리하여 개행 없이 출력
-
echo -nnn textn이 여러 개 있어도 하나의 옵션으로 처리하여 개행 없이 출력
-
echo -nnNn text- 이때는 옵션이 무효되며,
-nnNn text\n출력
- 이때는 옵션이 무효되며,
-
echo -n text -n more_text- 첫 번째
-n은 옵션으로 인식하고 이후-n은 일반 문자열로 출력
- 첫 번째
ft_env
-
env- 인자가 없으면 모든 환경 변수를 출력
key=value형식으로 출력되며,=가 포함되지 않은 변수는 출력되지 않음- 환경 변수가 없을 경우 에러 출력
-
env arg- 인자가 있으면
env: too many arguments에러 출력
- 인자가 있으면
ft_exit
-
exit- 인자가 없으면 기존 종료 코드(전역 변수)로 쉘을 종료
-
exit numnum이 숫자면num % 256값으로 쉘 종료num이 숫자가 아니면"exit: numeric argument required"에러 출력 후 종료 (코드255)
-
exit num1 num2 ...- 인자가 2개 이상이면
"exit: too many arguments"에러 출력 후 쉘을 종료하지 않음
- 인자가 2개 이상이면
-
malloc오류 발생 시"malloc error"출력 후 종료 (코드12)
-
예제 케이스
| 입력 값 | 반환 값 | 비고 | |---------|--------|------| |" 12342 241 "|1| 공백 포함 여러 숫자는 허용되지 않음 | |" 3 "|0| 유효한 숫자 | |" 3a "|1| 숫자 뒤에 문자가 포함되면 오류 | |" 3 4 "|1| 여러 개의 숫자가 포함되면 오류 | |" 3 4a "|1| 숫자 + 문자 조합은 오류 | |" 3 4 5 "|1| 여러 개의 숫자는 허용되지 않음 | |"lkj1234214"|1| 숫자로 시작하지 않으면 오류 |
ft_export
-
export- 인자가 없으면 환경 변수를 정렬하여
declare -x key="value"형식으로 출력 - 환경 변수가 없으면
"No such file or directory"에러 출력
- 인자가 없으면 환경 변수를 정렬하여
-
export key=valuekey가 유효하면 환경 변수 추가 또는 갱신key가 유효하지 않으면"not a valid identifier"에러 출력
-
export key- 값 없이
key만 입력하면 빈 값("")을 가진 환경 변수로 등록
- 값 없이
-
export key=value key2=value2 ...- 여러 개의 환경 변수를 한 번에 추가 가능
ft_pwd
- 현재 작업 디렉토리를 출력 (
getcwd()사용) getcwd()가 실패하면"Failed to get current directory."에러 출력 후g_signal_error = 1설정
ft_unset
-
unset key- 환경 변수에서
key를 삭제 key가 존재하지 않아도 에러 없이 넘어감
- 환경 변수에서
-
unset key1 key2 ...- 여러 개의 환경 변수를 한 번에 삭제 가능
-
unset(인자 없음)- 아무 동작도 하지 않음
-
유효하지 않은
key입력 시"unset: 'key': not a valid identifier"에러 출력
5. Evaluation
2025.05 코드 리뷰 추가 삽입
try1 - review 1

try1 - reivew 2

try1 - review 3

과제 크기가 크다 보니 평가 한번 받는데 기본 1시간 이상 걸렸던 것 같다. 해당 과제에서도 수많은 leak과 세그먼트 오류들을 마주했다. 특히 파싱 담당하신 팀원분이 상당히 고생이 많으셨다.
평가 중 특정 상황에서 abort 가 발생하는 문제가 발생했다. 아마 그 시점부터 1시간 동안 gdb를 켜서 평가자분과 함께 한줄 한줄 따라갔던 것으로 기억한다. 다만 평가지에 readline 내부의 문제점이 있을 수도 있다고 적혀있었고, 유일하게 의심스러운 부분이 readline 쪽에서 발생했기 때문에 평가를 마쳤다.
팀 과제이기도 했고, 함수와 파일이 정말 많아지기도 해서 이름들이 좀 헷갈리는 경우가 많았다. 그래서 중간 지점에 제미나이로 함수 이름을 추천 받아 반영했다. 가독성이 나쁘지 않았다.
6. Reference
- https://www.freebsd.org
- https://man7.org/linux/man-pages/man2/pipe.2.html
- https://millar-library99.tistory.com/21
- https://tsi.kr/entry/Minishell-미니쉘-테스트-할만한거