As beautiful as a shell
42서울 본과정 입과 후 여덟번째로 수행한 과제로, 작은 크기의 shell을 구현하는 과제이다. 우리는 이제 간단한 빌트인 함수부터 여러 명령어를 수행할 수 있는 간단한 shell을 만들어야 한다. 동굴 벽에 흐릿하게 비치는 bash를 구현하는 것이 목표인 것이다. 너무 큰 shell을 만들기 위해서 노력하다가 블랙홀에 빠지지 않게 조심하자.
shell의 내부 구현 (fork
와 execv
를 활용한 명령어 처리 등) 은 이미 이전 과제인 pipex 과제에서 진행한 바 있다. 따라서 pipex의 핵심 로직을 바탕으로 각종 명령어와 파싱부분을 구현해 조합하는 프로젝트가 되겠다. 컴파일러 개론을 들었거나, 시간적 여유가 많았다면 파싱부분도 깔끔하게 트리로 할 수 있었겠지만 현실적인 문제로 도전하지 못한 것이 아쉽다. 다행히 파싱부분은 뛰어난 팀원분께서 인고의 예외처리를 통해 구현해주셨다!
minishell
commands...
명령 대기 중 프롬프트 표시: 새 명령을 기다릴 때 프롬프트를 표시해야함!
작업 이력 관리: 이전 명령의 작업 이력을 관리할 수 있어야 함!
실행 파일 검색 및 실행: 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 쪽에서 발생했기 때문에 평가를 마쳤다.
팀 과제이기도 했고, 함수와 파일이 정말 많아지기도 해서 이름들이 좀 헷갈리는 경우가 많았다. 그래서 중간 지점에 제미나이로 함수 이름을 추천 받아 반영했다. 가독성이 나쁘지 않았다.