source와 bash 그리고 shell

이정진·2024년 12월 4일

0. 개요

linux에서 쉘 스크립트를 작성하고 명령어를 통해 해당 스크립트를 실행하는 방법에는 2가지가 있다.

  1. source 명령어를 활용해서 실행
  2. bash 명령어를 활용해서 실행

그렇다면 두 명령어에는 어떤 차이가 있는지 궁금해서 찾아보게 되었다.

1. source 명렁어

아래는 source command에 관한 description이다.

	Execute commands from a file in the current shell.
    
    Read and execute commands from FILENAME in the current shell.  The
    entries in $PATH are used to find the directory containing FILENAME.
    If any ARGUMENTS are supplied, they become the positional parameters
    when FILENAME is executed.
    
    Exit Status:
    Returns the status of the last command executed in FILENAME; fails if
    FILENAME cannot be read.

위 내용을 살펴보면 source command는 아래와 같은 특징을 가지고 있다.

  1. 현재 shell에서 명령을 읽고 실행한다.
  2. Filename이 포함된 디렉토리는 $PATH 환경 변수를 통해 찾는다.
  3. 만약 인수가 제공되어 있다면 파일이 실행될 때 위치 매개변수가 된다.
  4. 파일의 마지막 명령의 종료된 상태가 반환된다.

1.1 Command의 실행과 $PATH

위에서 $PATH라는 환경 변수를 통해 Command File을 찾는다고 했다. Command가 실행되는 과정을 $PATH 중심으로 확인해보려고 한다.

  1. 우선 명령어가 입력됐을 때 '/'가 포함되어있지 않는다면 shell은 쉘 함수를 먼저 찾고자 한다.
  2. 만약 매칭되는 함수가 없다면 shell은 내부 명령어를 찾는다.
  3. 만약 2번까지의 과정에서도 Command를 찾지 못했다면 $PATH 환경변수에서 해당 파일을 가지고 있는 디렉토리를 찾는다.
  4. 3번까지의 과정에서도 찾지 못했다면 command not found를 출력한다.

실제로 $PATH 환경변수를 확인해보았다.

echo $PATH
/root/.nvm/versions/node/v20.11.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:
/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:...

위와 같이 출력되며 :를 통해 디렉토리를 구분한다. 출력된 내용을 보면 /usr/bin이라는 디렉토리가 있다. echo 명령어의 위치를 찾아보면 아래와 같이 출력된다.

which -p echo
/usr/bin/echo

echo 명령어는 $PATH 환경변수에 포함된 /usr/bin 폴더에 존재하기에 실행할 수 있는 것이다.

root 디렉토리의 하위 폴더인 scripts에 test.sh를 만든 뒤 home 디렉토리에서 실행하면

source test.sh
source: no such file or directory: test.sh

처럼 찾을 수 없다는 메세지가 뜬다. 이때, $PATH의 환경변수에 일시적으로 test.sh가 들어있는 ~/scripts 폴더를 추가시키고 test.sh를 실행시키면

export PATH=$PATH:~/scripts 
source test.sh
hello

test.sh의 내용인 echo hello가 제대로 실행되는 것을 확인할 수 있다.

1.2 shell의 의미?

위에서 source command는 현재 쉘에서 실행된다는 점을 알 수 있었다. 그렇다면 쉘이 여러 개일 수도 있는 것일까?

GNU Operating System의 설명에 따르면 shell은 명령을 실행하는 macro processor라고 설명하고 있다. 즉, shell은 process의 일종이라는 것을 의미한다.

이제 source를 통해 실행되는 쉘 스크립트의 PID와 shell 정보를 확인해보자.

test.sh
ps -f -p $$
readlink -f /proc/$$/exe
ps -f -p $$ && readlink -f /proc/$$/exe && source test.sh
#UID          PID    PPID  C STIME TTY          TIME CMD
#root         656     655  0 21:14 pts/0    00:00:02 -zsh
#/usr/bin/zsh
#UID          PID    PPID  C STIME TTY          TIME CMD
#root         656     655  0 21:14 pts/0    00:00:02 -zsh
#/usr/bin/zsh

위의 코드는 실행 중인 process의 정보와 함께 symbolic Link를 이용해 현재 실행 중인 shell의 절대 위치를 출력하는 코드를 작성했다. PID와 Parent PID, shell의 위치가 동일한 것을 알 수 있다.

1.3 파일의 종료된 상태 반환

source는 같은 쉘에서 실행되기에 파일이 반환한 결과가 해당 쉘에 반영된다. $PATH 환경 변수를 수정한 뒤 생기는 변화를 관찰해보겠다.

#이전 $PATH 환경 변수
... :/mnt/c/Python312:/mnt/c/Users/earth/AppData/Roaming/npm

#home에서 test.sh 실행
source test.sh
#source: no such file or directory: scripts

현재 $PATH의 환경변수에 /root/scripts가 없어 home에서 test.sh가 실행되지 않는다.

#test.sh 내용
export PATH=$PATH:~/scripts

#test.sh 실행
source ~/scripts/test.sh

#환경 변수 확인
echo $PATH
... :/mnt/c/Python312:/mnt/c/Users/earth/AppData/Roaming/npm:/root/scripts

#home에서 test.sh 실행
source test.sh
#에러 코드 없음

test.sh를 source 통해 실행시킴으로써 $PATH에 /root/scripts를 추가했으며 home에서 test.sh를 실행하니 제대로 실행된다.

즉, source는 현재 쉘과 같은 쉘에서 실행되기에 반환된 결과가 해당 쉘에 반영되는 모습을 보여준다.

2. bash 명령어

아래는 GNU.org에서 bash command에 대한 description 중 일부를 발췌한 내용이다.

	Shells may be used interactively or non-interactively. In interactive mode,
    they accept input typed from the keyboard. When executing non-
    interactively, shells execute commands read from a file.

	if filename is an executable shell script. This subshell reinitializes itself,
	so that the effect is as if a new shell had been invoked to interpret the
	script, with the exception that the locations of commands remembered by the
	parent (see the description of hash in Bourne Shell Builtins) are retained by the child.

bash 명령어의 특징은 아래와 같다.

  1. shell은 interactive 혹은 non-interactive의 명령어 실행 모드가 있다.
  2. shell을 실행할 때 subshell을 생성하고 자체적으로 재초기화한다.

2.1 interactive non-interactive shell

#interactive mode
$ ps -f -p $$
#UID          PID    PPID  C STIME TTY          TIME CMD
#root       37850   37845  0 16:47 pts/3    00:00:01 -zsh

$ bash -i
root$ echo $ps -f -p $$
#UID          PID    PPID  C STIME TTY          TIME CMD
#root       48969   37850  0 17:32 pts/3    00:00:00 bash -i

#non-interactive mode
$ bash test.sh

interactive mode의 경우 non-interactive mode와 동일하게 새로운 쉘이 생성되지만 사용자와 상호작용이 가능한 쉘이 생성된다. non-interactive의 경우 쉘 스크립트의 내용만이 실행된다.

2.2 Subshell

#test.sh
ps -f -p $$
readlink -f /proc/$$/exe
ps -f -p $$ && readlink -f /proc/$$/exe && bash test.sh
#UID          PID    PPID  C STIME TTY          TIME CMD
#root         615     614  0 14:16 pts/0    00:00:01 -zsh
#/usr/bin/zsh
#UID          PID    PPID  C STIME TTY          TIME CMD
#root       27671     615  0 16:07 pts/0    00:00:00 bash test.sh
#/usr/bin/bash

앞서 살펴본 Source와는 달리 참조하는 shell이 다르며 bash가 zsh를 parent shell로 가지고 있음을 확인할 수 있다.

그렇다면 독립된 shell(자식 shell)에서 반환된 결과가 parent shell에 영향을 미치는지 확인해보자.

#이전 $PATH 환경 변수
... :/mnt/c/Python312:/mnt/c/Users/earth/AppData/Roaming/npm

#home에서 test.sh 실행
source test.sh
#source: no such file or directory: scripts

위에서와 마찬가지로 현재 $PATH의 환경변수에 /root/scripts가 없어 home에서 test.sh가 실행되지 않는다.

#test.sh
export PATH=$PATH:~/scripts

#test.sh 실행
bash ~/scripts/test.sh

#환경 변수 확인
echo $PATH
... :/mnt/c/Python312:/mnt/c/Users/earth/AppData/Roaming/npm

#home에서 test.sh 실행
bash test.sh
#bash: test.sh: No such file or directory

parent shell의 환경변수에는 변화가 없었으며 home에서의 test.sh 또한 실행되지 않았다.

즉, bash 명령어를 통해 생성된 subshellparent shell독립적인 환경이다.

3. source와 bash의 차이

위에서 살펴봤을 때 두 명령어 간의 가장 큰 차이는 같은 쉘을 사용하느냐의 유무이다. source는 같은 쉘을 통한 단일 프로세스를 사용하기 때문에 해당 shell의 context를 공유한다. 즉, shell의 환경설정 혹은 새로운 변수, 함수를 추가하는 데 용이하다.

반면 bash는 subshell이라는 새로운 프로세스를 생성하기 때문에 독립적인 실행 context를 가진다. 따라서 독립적인 스크립트를 실행하거나 테스트 환경이 필요할 때 주로 사용된다.

이를 표로 정리하면 아래와 같다.

특성sourcebash
실행 환경현재 셸 환경에서 실행새로운 하위 셸을 생성하여 실행
변수 영향현재 셸의 환경 변수가 수정됨부모 셸의 환경에 영향을 주지 않음
스크립트 종료 후변경된 환경이 유지됨변경사항이 하위 셸과 함께 소멸됨
주요 용도환경 설정 파일 로드(.bashrc, .profile 등)
현재 셸에 변수/함수 추가
독립적인 스크립트 실행
격리된 환경에서의 테스트
메모리 사용추가 프로세스 생성 없음새로운 프로세스 생성
실행 속도상대적으로 빠름source보다 느림 (프로세스 생성 필요)

4. shell이 무엇인가?

위의 내용을 종합하면 shell은 컴퓨터와 사용자를 연결하는 하나의 process를 의미한다. 이 process는 명령어를 해석하는 역할을 수행할 뿐 아니라 환경 변수를 통해 하나의 context를 구축할 수 있으며 독립적인 프로세스 환경을 통해 안전한 실행 환경을 제공한다. 자식과 부모의 계층 구조를 가진다. 또한 스크립트를 통한 자동화로 사용자를 위한 인터페이스도 제공하는 역할을 수행한다.

참고문헌
https://www.gnu.org/software/bash/manual/bash.html
https://linuxcommand.org/lc3_man_page_index.php#other

0개의 댓글