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

try1 - reivew 2

try1 - review 3

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