draft = true
As beautiful as a shell
42서울 본과정 입과 후 여덟번째로 수행한 과제로, 작은 크기의 shell을 구현하는 과제이다. 우리는 이제 간단한 빌트인 함수부터 여러 명령어를 수행할 수 있는 minishell을 만들어야 한다. 너무 큰 shell을 만들기 위해서 노력하다가 블랙홀에 빠지지 않게 조심하자!
minishell
commands...
명령 대기 중 프롬프트 표시: 새 명령을 기다릴 때 프롬프트를 표시해야함!
작업 이력 관리: 이전 명령의 작업 이력을 관리할 수 있어야 함!
실행 파일 검색 및 실행: PATH
변수를 기반으로 올바른 실행 파일을 검색하고 실행해야하고, 상대 경로나 절대 경로를 사용할 수 있어야 한다.
신호 처리: 전역 변수 사용은 오로지 하나!
해석하지 말아야 할 항목: 닫히지 않은 인용부호(따옴표)나 주제와 관련 없는 특수 문자(\
, ;
)는 해석하지 않아야 함…
따옴표 처리:
싱글 쿼트 ('
): 인용된 시퀀스에서 메타문자가 해석되지 않도록 해야 함
더블 쿼트 ("
): $
를 제외한 메타문자가 해석되지 않도록 해야 함
리디렉션 구현:
<
: 입력을 리디렉션
>
: 출력을 리디렉션
<<
: 구분자를 지정한 다음, 구분자를 포함한 줄을 읽을 때까지 입력을 받아야 하고, (이 작업은 이력에 기록되지 않아야 한다.)
>>
: 출력을 추가 모드로 리디렉션
파이프라인 구현: |
로 각 명령의 출력을 다음 명령의 입력으로 연결해야 함.
환경 변수 처리: $
로 시작하는 문자열은 해당 값으로 확장되어야 함.
특수 변수 처리: $?
: 최근에 실행된 포그라운드 파이프라인의 종료 상태로 확장되어야 함.
특수 키 처리:
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와 사실상 유사하기 때문에 익숙한 작업이 주를 이룬다.
우리 팀의 경우 내가 실행부 + 빌트인함수
를 맡았고, 함께한 팀원분이 파싱부 + here_doc 등
을 맡았다.
이에 따라, 이번 과제 정리는 내가 담당한 빌트인 함수만 주로 다루어보고자 한다. (실행부는 pipex와 유사). 코드를 실제로 나열하기보다는 각 함수들이 어떠한 특징을 가지고 있는지 알아보자! 함수들의 작동은 모두 bash shell을 기준으로 작동을 확인해보자.
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"
에러 출력