Login vs Non-Login Shell

이번엔 좀 다른 관점에서 shell 이 실행되는 환경을 두가지로 나누어 볼 수 있습니다. 리모트 서버에 ssh 이나 telnet 으로 접속해서 login 과정을 거쳐 사용하게 되는 login shell 과 윈도우 매니저에서 메뉴로 제공하는 터미널 프로그램을 실행시켜 사용하게 되는 non-login shell 입니다.

login shell 인지 아닌지는 shopt -q login_shell; echo $? 을 통해 알아볼 수 있습니다.
profile, bashrc 등의 파일에는 사용자가 원하는 쉘 스크립트를 작성할 수 있습니다.

Login Shell

  • 시작시 /etc/profile, ~/.bash_profile, ~/.bash_login, ~/.profile 순서로 파일을 읽어들여 실행합니다. 보통 /etc/profile 에서는 . ( source 명령 ) 을 이용해 /etc/bash.bashrc 를 읽어들이고 ~/.profile 에서는 ~/.bashrc 파일을 읽어들입니다. (bash --login -c 와 같이 실행할 경우도 적용됩니다 ).

.profile 과 .bash_profile 두 가지가 존재하는 이유는 shbash 를 구분하기 위해서 입니다. 가령 bash 가 사용될 경우 .bash_profile 파일이 존재하면 .profile 파일은 실행되지 않습니다.

  • logout ( 또는 exit ) 시에 ~/.bash_logout 파일을 실행합니다.

  • shopt -s huponexit 옵션 설정을 통해 logout 시에 background 로 실행되는 job 들에게 HUP 시그널을 보내 종료하게 할 수 있습니다.

  • logout builtin 명령을 사용할 수 있습니다.

  • 프롬프트 상에서 echo $0 를 실행했을때 -bash 와같이 첫 문자가 - 이면 login shell 을 나타냅니다.

Non-Login Shell

  • 시작시 /etc/bash.bashrc~/.bashrc 파일을 실행합니다.

.rc 파일의 유래: 1965 년 MIT Compatible Time-Sharing System (CTSS) 에는 하나의 파일에 여러 명령을 넣어놓고 실행하는 기능이 있었는데 'run commands' 를 뜻하는 의미로 'runcom' 이라 불렀다고 합니다. 여기서 앞 글자를 따서 .rc 파일 이라고 합니다.

  • non-login shell 에서도 bash -l , sh -l, sudo, su 명령을 통해서 login shell 을 만들 수 있습니다.

$ shopt login_shell          # 현재 터미널에서의 상태
login_shell     off
$ echo $0
/bin/bash

$ sudo -i                    $ sudo -s                    $ su - mug896
# shopt login_shell          # shopt login_shell          Password: ******
login_shell     on           login_shell     off          $ shopt login_shell 
# echo $0                    # echo $0                    login_shell     on
-bash                        /bin/bash                    $ echo $0
                                                          -bash
$ bash --login
$ shopt login_shell 
login_shell     on
$ echo $0
bash

Real user id vs Effective user id

Real user id 는 시스템에 로그인했을 때의 id 를 말하며 $UID readonly 환경 변수에 저장됩니다. effective user id 는 set uid 비트가 설정된 프로그램을 실행했을 때 변경되며 $EUID readonly 환경 변수에 저장됩니다. $EUID 는 명령을 실행할 때 실질적인 권한에 해당합니다.

### alice 계정 ###
alice@box:/tmp$ cp /bin/bash bash
alice@box:/tmp$ chmod u+s bash    # set uid 비트 설정

### bob 계정 ###
bob@box:/tmp$ ./bash -p
bash-4.3$ id                      # euid 가 alice 로 변경되었다
uid=1002(bob) gid=1005(gamers) euid=1004(alice) groups=1002(bob),1005(gamers)

.bashrc.d 만들어 사용하기

shell 을 사용하다 보면 내가 만든 함수, alias, 자동완성 이 쌓이게 되는데요. 이럴때 ~/.bashrc.d 디렉토리를 만들어서 한곳에서 관리하면 좋습니다. 이름 뒤에 .d 를 붙인것은 .bashrc 에서 사용하는 파일들이 위치하는 디렉토리 라는 뜻입니다.

# ~/.bashrc 에 들어갈 내용

# alias 설정 읽어들이기
for file in ~/.bashrc.d/aliases/*.sh
do
    source "$file"
done

# 함수 설정 읽어들이기
for file in ~/.bashrc.d/functions/*.sh
do
    source "$file"
done

# 자동완성 설정 읽어들이기
for file in ~/.bashrc.d/completions/*.sh
do
    source "$file"
done

unset -v file

$PATH 환경 변수

외부 명령을 실행할 때 검색할 디렉토리를 $PATH 환경 변수에 등록해 사용하는데요. 만약에 동일한 이름의 명령이 두 곳의 디렉토리에 중복되어 나타날 경우 앞쪽에 위치한 디렉토리에 있는 명령이 우선순위를 갖게 됩니다.

Quiz

스크립트 파일은 외부에 파일로 존재하므로 ../../test.sh , bin/test.sh 등과 같이 여러 가지 경로 지정 방법을 통해 실행시킬 수가 있습니다. 또한 symbolic link 와 연결될 수도 있는데요. 스크립트 내에서 자기 자신의 전체 경로와 physical 절대 경로를 구하려면 어떻게 할까요?

기본적으로 $PWD$0 를 합하면 전체경로가 됩니다.

#!/bin/sh
echo '                          $0 : '"$0"
echo '                        $PWD : '"$PWD"
echo '$( cd "${0%/*}/" && pwd -P ) : '"$( cd "${0%/*}/" && pwd -P )"
echo 
echo "           스크립트 파일 전체경로 : $PWD/$0"
echo
echo "           스크립트 파일 절대경로 : $( cd "${0%/*}/" && pwd -P )/${0##*/}"
echo '             readlink -f $0  : '"$( readlink -f "$0" )"

------------------------------------------------------------------------

$ ln -s /usr/local/bin /home/mug896/foo      # 홈 디렉토리에 foo 링크 생성
$ cp test.sh /home/mug896/foo/               # /usr/local/bin 으로 test.sh 파일 복사
$ cd /home/mug896/tmp                        # tmp 디렉토리로 이동해서 test.sh 실행

$ ../foo/test.sh
                          $0 : ../foo/test.sh
                        $PWD : /home/mug896/tmp
$( cd "${0%/*}/" && pwd -P ) : /usr/local/bin

           스크립트 파일 전체경로 : /home/mug896/tmp/../foo/test.sh

           스크립트 파일 절대경로 : /usr/local/bin/test.sh
             readlink -f $0  : /usr/local/bin/test.sh

foo/../bar//./test.sh 와 같은 형태도 유효한 경로입니다.
foo/../bar//./test.sh ---> foo/../bar/test.sh ---> bar/test.sh 가 된다.