shell

hyenam·2021년 12월 9일
0

minishell

목록 보기
5/6

본 글은 bash 매뉴얼 번역본을 통해 minishell을 구현하는데에 있어서 필요할 것 같은 부분들만 내용을 짜집기한 글 입니다.

셸이란

간단하게 설명하면 명령을 실행하는 매크로 처리기이다.

매크로 처리기란 텍스트와 심볼을 전개하여 더 큰 표현식을 만드는 장치

셸의 구성요소

  • Shell Syntax : 셸에게 입력한 내용의 의미.
  • Shell Commands : 사용할 수 있는 명령어의 타입.
  • Shell Functions : 이름으로 명령어를 그룹으로 만들기.
  • Shell Parameters : 변수가 값을 저장하는 방법.
  • Shell Expansions : Bash가 매개변수를 전개하는 방법과 사용할 수 있는 다양한 전개에 대해서.
  • Redirections : 입력과 출력을 제어하는 방법.
  • Executing Commands : 명령어를 실행할 때 벌어지는 일.
  • Shell Scripts : 셸 명령어가 들어있는 파일을 실행하기.

Shell Syntax

셸의 작동원리

  1. 파일로부터 읽거나 -c 옵션을 통해서 문자열을 받거나, 또는 사용자의 터미널로부터 입력을 받습니다.
  2. 입력을 단어와 연산자로 분해하고, 따옴표로 감싸기 규칙을 적용한다. 이 토큰들은 메타 문자로 분리되며, 별칭(alias) 전개가 이루어집니다.
  3. 토큰들을 간단한 복합 명령어로 구문 분석합니다.
  4. 파일 이름, 명령어, 인수의 리스트로 토큰을 전개하는 등의 다양한 셸 전개를 실행합니다.
  5. 필요한 리다이렉션을 수행하고 리다이렉션 연산자와 그 피연산자를 매개변수 리스트에서 제거합니다.
  6. 명령을 실행합니다.
  7. 필요하다면 명령의 실행을 기다려 종료 스테이터스를 취득할 수도 있습니다.

토큰: 셸에서 하나의 단위로 인식되는 연속된 문자들. 단어이거나 연산자.
메타문자: 따옴표를 제거했을 때 단어로 분리되는 문자들. 메타 문자는 스페이스, 탭, 개행이나 다음 중 하나입니다 |, &, ;, (, ), <, >
연산자: 제어 연산자나 리다이렉션 연산자
단어: 셸에서 하나의 단위로 처리되는 연속된 문자들. 단어는 따옴표가 제거된 메타문자를 포함하지 않을 수도 있습니다.

Quoting

Single Quotes

  • 메타문자가 들어와도 메타문자가 아닌 일반 문자열로 다룸
  • \를 사용하더라도 ' 출력 불가능

Double Quotes

  • 메타문자를 일반 문자열로 다루긴하지만 $, `, \는 예외 (이력 전개 기능이 켜져있다면 !도 포함)

    이력전개 기능 (Bash History Facilities)
    set 빌트인에 -o history 옵션을 켜짐으로 설정했을 때, 이전에 입력한 명령어 리스트인 명령어 이력에 접근할 수 있도록 한다

Shell Commands

복잡한 셸 명령어는 단순 명령어를 다양한 방식으로 조합하여 구성됩니다. 파이프라인을 사용해 한 명령어의 출력을 다음 명령어의 입력으로 사용하거나, 루프나 조건부 구조를 사용하거나, 다른 방식으로 그룹화할 수 있습니다.

  • Reserved Words : 셸에게 있어 특별한 의미를 가지는 단어.
  • Simple Commands : 가장 흔한 명령어의 타입.
  • Pipelines : 여러 명령어의 입력과 출력을 연결하기.
  • Lists of Commands : 명령어를 순서대로 실행하는 방법.
  • Compound Commands : 제어 흐름을 이용하는 셸 명령어.
  • Coprocesses : 명령어 간의 양방향 통신.
  • GNU Parallel : 명령어를 병렬로 실행하기.

Pipelines

파이프라인이란 제어 연산자의 하나인 '|', '|&'를 사용하여 나눠지는 하나 이상의 명령어 나열을 의미합니다.

파이프라인의 각 명령어의 출력은 파이프를 통해 다음 명령어의 입력으로 연결됩니다. 각 명령어는 직전 명령의 출력을 읽습니다. 명령어에 의해서 어떤 리다이렉션이 지정되기 전에만 이루어집니다.('|&'는 예외)

파이프라인의 종료 스테이터스는 pipefail 옵션을 켠 상태가 아니라면 파이프라인의 마지막 명령어의 종료 스테이터스입니다.

Shell Parameters

Special Parameters

특수 문자를 사용하는 매개변수.
변수가 설정되면 unset 빌트인 명령어로만 제거할 수 있습니다.

  • ?: ($?) 가장 최근에 실행된 포그라운드 파이프라인의 종료 스테이터스로 전개합니다.

    최근에 실행된 명령어, 함수, 스크립트 자식의 종료 상태를 0과 1로 나타냄

Redirections

리다이렉션을 이용해 파일 핸들을 복제, 열기, 닫기, 다른 파일에 대한 참조 만들기 그리고 명령어가 읽고 쓸 파일을 변경할 수 있습니다.

또한 리다이렉션은 현재 셸 실행 환경의 파일 핸들을 변경하기 위해서 사용할 수 있습니다. 다음의 리다이렉션 연산자는 앞에 두거나, 뒤에 두거나, 단순한 명령이라면 임의의 위치에 둘 수 있습니다.

리디렉션은 나타나는 순서대로 왼쪽에서 오른쪽으로 처리됩니다.

리다이렉션 연산자의 첫글자가
'<'라면, 리다이렉션은 표준 출력(파일 디스크립터 0)을 참조하며,
'>'라면 리다이렉션은 표준 출력(파일 디스크립터 1)을 참조합니다.

Redirecting Input

입력 리다이렉션은 word 를 전개하여 얻은 이름을 사용하는 파일을 열고, 파일 디스크립터 n으로부터 읽을 수 있도록 합니다. n이 지정되지 않았다면 표준 입력(파일 디스크립터 0)을 사용합니다.

[n]<word

Redirecting Output

출력 리다이렉션은 word 를 전개하여 얻은 이름을 사용하는 파일을 열고, 파일 디스크립터 n이 쓸 수 있도록 합니다.

n이 지정되지 않았다면 표준 출력(파일 디스크립터 1)을 사용합니다.

대상 파일이 존재하지 않는다면 만듭니다. 만약 파일이 존재하는 경우 파일의 내용물을 지우고 빈 파일로 만듭니다. (덮어쓰기)

[n]>[|]word

Appending Redirected Output

word를 전개하여 얻은 파일을 열고, 파일 디스크립터 n이 추가로 쓸 수 있도록 합니다. (이어쓰기)

n이 지정되지 않았다면 표준 출력(파일 디스크립터 1)을 사용합니다. 대상 파일이 존재하지 않는다면 만듭니다.

[n]>>word

Here Documents

셸에게 현재 소스로부터 (뒤따라오는 공백 문자가 없이) word를 포함하는 줄이 나타날 때까지를 입력으로 읽도록 지시합니다.

읽어들인 모든 줄은 표준 입력(파일 디스크립터 n이 지정되어 있다면 n)인 것처럼 취급됩니다.

[n]<<[-]word
        here-document
delimiter

Executing Commands

  • Simple Command Expansion : Bash가 단순 명령어를 실행하기 전에 이를 전개하는 방법.
  • Command Search and Execution : Bash가 명령어를 찾고 실행하는 방법.
  • Command Execution Environment : 셸 빌트인이 아닌 명령어를 실행할 때의 환경.
  • Environment : 명령어의 환경.
  • Exit Status : 명령어가 반환하는 스테이터스와 Bash가 스테이터스를 처리하는 방법.
  • Signals : Bash나 명령어가 시그널을 받았을 때 일어나는 일.

Simple Command Expansion

단순 명령어를 실행할 때, 셸은 다음의 전개, 할당, 리다이렉션을 왼쪽에서 오른쪽으로 순차실행합니다.

단순 명령어를 실행할 때, 셸은 다음의 전개, 할당, 리다이렉션을 왼쪽에서 오른쪽으로 순차실행합니다.

  1. 파서가 (명령어 앞에 오는) 변수 대입으로 인식한 단어와 리다이렉션은 나중의 처리를 위해서 보존됩니다.
  2. 변수 대입이나 리다이렉션이 아닌 단어들은 전개가 적용됩니다(Shell Expansions를 참조). 전개 후에도 남아있는 단어는 첫번째 단어가 명령어, 나머지가 인수로 처리됩니다.
  3. 이전에 설명한 리다이렉션이 실행됩니다(Redirections를 참조).
  4. 각 변수 대입에서 '=' 뒤에 오는 텍스트에는 대입이 이루어지기 전에 물결줄표 전개, 매개변수 전개, 명령어 치환, 산술 전개, 따옴표 제거가 실행됩니다.

전개한 결과에 명령어가 존재하지 않을 경우, 변수 대입은 현재의 셸 환경에 적용됩니다. 그렇지 않으면 변수는 실행된 명령어의 환경에 추가되며, 현재의 셸 환경에는 영향을 주지 않습니다. 읽기 전용 변수에 대입을 시도한 경우, 명령어에서는 에러가 발생하며 0 이외의 스테이터스와 함께 종료됩니다.

명령어가 존재하지 않는 경우, 리다이렉션은 실행되지만 현재의 셸 환경에는 영향을 주지 않습니다. 리다이렉션 에러는 명령어가 0 이외의 스테이터스로 종료시킵니다.

전개 후에도 명령어가 남아있다면 다음 처리로 넘어가며, 남아있지 않다면 명령어 실행이 종료됩니다. 전개에 명령어 치환이 포함되어 있는 경우, 명령어의 종료 스테이터스는 마지막에 실행된 명령어 치환의 종료 스테이터스가 됩니다.

Command Search and Execution

명령어가 단어로 분리된 이후, 그 결과가 단순 명령어와 인수 리스트라면 다음 동작을 수행합니다.

  1. 명령어의 이름이 슬래시를 포함하고 있지 않다면, 셸은 해당 명령어를 찾습니다. 만약 그 이름을 가지는 셸 함수가 존재한다면 Shell Functions에서 설명된 대로 실행합니다.
  2. 해당 이름을 가지는 함수가 존재하지 않는다면, 셸은 셸 빌트인 명령어 목록을 검색합니다. 만약 일치하는 명령어가 있다면 해당 빌트인 명령어를 호출합니다.
  3. 셸 함수와 빌트인 명령어에 일치하는 이름이 없고, 이름에 슬래시도 포함하고 있지 않다면, Bash는 $PATH의 각 요소로부터 해당 이름을 가진 실행가능한 파일이 존재하는 디렉터리를 찾습니다. Bash는 PATH 검색을 여러번 검색하는 것을 피하기 위해서, 실행가능한 파일의 전체 경로명을 해시 테이블을 사용해서 기억합니다(hash에 대한 설명은 Bourne Shell Builtins를 참조). 해시 테이블에서 명령어를 찾지 못했을 때에만 $PATH의 각 디렉터리에 대해 전체 검색을 수행합니다. 만약 검색에 실패하면, 셸은 command_not_found_handle라는 이름의 셸 함수가 정의되어 있는지 찾습니다. 만약 존재한다면, 이 함수는 원래의 명령어와 원래의 명령어에 넘겨진 인수를 넘겨받고, 별도의 실행 환경에서 호출되며, 이 함수의 종료 스테이터스는 서브셸의 종료 스테이터스가 됩니다. 만약 이 함수가 정의되어있지 않다면, 셸은 에러 메시지를 출력하고 종료 스테이터스 127과 함께 종료합니다.
  4. 검색에 성공하거나, 명령어 이름이 하나 이상의 슬래시를 포함하고 있는 경우, 셸은 해당 이름의 프로그램을 별도의 실행 환경에서 실행합니다. 0번째 인수에는 주어진 이름이 설정되며, 만약 넘겨받은 인수가 있다면 명령어의 나머지 인수로 설정합니다.
  5. 만약 지정된 파일이 실행 가능한 형식이 아니거나 파일이 디렉터리에 존재하지 않는다는 이유로 실행에 실패하는 경우, 이는 셸 스크립트인 것으로 취급하며 셸은 Shell Scripts에서 설명된 대로 이를 실행합니다.
  6. 만약 명령어가 비동기로 실행된 것이 아니라면, 셸은 명령어의 실행이 완료되기를 기다리고 명령어의 종료 스테이터스를 수집합니다.

Environment

프로그램이 호출될 때, 환경이라고 불리는 문자열의 배열이 주어집니다. 이는 이름-값 쌍의 리스트로, name=value의 형식입니다.

셸은 호출시에 자신의 환경을 스캔하고 발견한 이름마다 매개변수를 생성한 뒤, 자식 프로세스로 익스포트할 매개변수를 자동으로 표시해둡니다. 실행된 명령어는 환경을 상속합니다.

환경의 매개변수의 값이 수정되면 새 값은 이전 값을 대체하고 환경의 일부가 됩니다. 실행된 명령어에 상속된 환경은 셸의 초기 환경을 구성하며, 이는 unsetexport -n 명령어를 통해 삭제할 수 있으며, export, declare -x를 통해 추가할 수 있습니다.

단순 명령어의 환경이나 함수는 Shell Parameters에서 설명된 것처럼 매개변수 대입을 앞에 두어서 일시적으로 변경할 수 있습니다. 이 대입식은 해당 명령어의 환경에만 영향을 미칩니다.

Exit Status

실행된 명령어의 종료 스테이터스는 waitpid 시스템 콜이나 이와 동등한 함수에 의해서 반환되는 값입니다.

종료 스테이터스는 0과 255사이의 값입니다만, 아래에서 설명하는 것처럼 셸은 125보다 큰 값을 특별한 의미로 사용합니다. 셸 빌트인 명령어와 복합 명령어의 종료 스테이터스는 이 범위로 제한됩니다.

셸에서는 종료 스테이터스 0으로 종료한 명령어는 실행 성공입니다. 0이 아닌 종료 스테이터스는 실패를 의미합니다.

Using History Interactively

Bash History Facilities

set 빌트인에 -o history 옵션을 켜짐으로 설정했을 때, 셸은 이전에 입력한 명령어 리스트인 명령어 이력에 접근할 수 있도록 합니다.

HISTSIZE 셸 변수의 값은 이력 리스트에 저장할 명령어의 개수를 지정합니다. 마지막 $HISTSIZE 명령어의 텍스트(기본값은 500)가 저장됩니다. 셸은 매개변수나 변수를 전개하기 전에 각 명령어를 이력 리스트에 저장합니다.

셸이 시작할 때, HISTFILE 변수(기본값은 ~/.bash_history)로 지정된 파일로 이력을 초기화합니다.
HISTFILE의 값에 지정된 파일은 필요에 따라서 저장하고 있는 이력이 HISTFILESIZE 변수의 값에 지정된 줄 수 이하가 되도록 여분을 잘라냅니다.

이력 기능이 활성화된 상태에서 셸을 종료할 때, 마지막 $HISTSIZE 개의 줄이 이력 리스트로부터 $HISTFILE로 지정한 파일에 복사됩니다.

histappend 셸 옵션이 켜져있다면, 이력 파일의 마지막에 추가되며, 옵션이 꺼져 있다면 이력 파일 전체를 덮어씁니다.
HISTFILE이 설정되어 있지 않거나 이력 파일에 쓰기 권한이 없을 때, 이력은 저장되지 않습니다.

이력을 저장한 후, 이력 파일은 $HISTFILESIZE 개의 줄보다 많은 줄을 포함하지 않도록 여분을 잘라냅니다. HISTFILESIZE가 설정되어 있지 않거나 또는 널, 숫자가 아닌 값, 0보다 작은 값이 설정되어 있다면, 이력 파일을 잘라내는 동작은 실행되지 않습니다.

셸은 어떤 명령어를 이력 리스트에 저장할지 제어할 수 있습니다. HISTCONTROLHISTIGNORE 변수를 설정하면 셸은 입력된 명령어의 일부만 저장합니다.

profile
공부한 걸 정리하고 있습니다.

0개의 댓글