리눅스 쉘 스크립트 맛보기
in Development on Linux
2.1 리눅스의 부팅 과정과 로그인 쉘
- init
- init과 연결된 수많은 프로세스들이 자신만의 프로세스 ID(PID)를 가지고 생성되기 시작
- init의 PID는 1
- init 프로세스가 시스템을 초기화하고 터미널 라인을 오픈하기 위한 작업을 시작하고, 표준 입력과 표준 출력, 표준 에러를 설정
ps -ef
를 입력하면 PID와 같이 출력- 표준 입력 (0): 키보드로부터 입력받는 것
- 표준 출력 (1) / 표준 에러(2) : 모니터로 출력하는 것
- 파일 디스크립터 0 1 2 ( 식별자 )
- 사용 사례 : bash 스크립트를 작성할 경우 표준 에러를 표준 출력으로 돌리고, 에러인 경우 dev/null로 찍는 경우 등등
- standard error redirection
- https://unix.stackexchange.com/questions/111611/what-does-the-rc-stand-for-in-etc-rc-d
- 그 이후 로그인 프롬포트를 보여줌
- init 프로세스는
/etc/rc.d/init.d
에 런레벨별로 설정되어 있는 쉘 스크립트를 실행. 이 스크립트들은chkconfig
명령을 사용해 부팅 시 자동으로 실행할 것인지, 실행하지 않을 것인지 설정할 수 있음 - 부팅 시 수행할 런레벨은
/etc/inittab
에 설정되어 있음( 우분투는/etc/init/rc-sysinit.conf
!) - 런레벨은 0부터 6까지 존재하는데, 주로 텍스트 모드 런레벨인 3번(주로 서버)과 그래픽 모드 런레벨인 5번을 주로 사용(주로 데스크탑) 현재는 level 2를 사용 중
rc1.d
,rc2.d
,rc3.d
,rc4.d
,rc5.d
,rc6.d
에 설정 파일이 들어가있음 (runlevel configuration)- K로 시작하는 것은 kill, S로 시작하는 것은 Start를 의미하며 파일의 prefix를 수정하면 바로 반영
- 로그인 프롬포트
/bin/login
프로그램이/etc/passwd
파일에 있는 첫 번째 필드를 체크하기 위해 유저 아이디를 검증하고, 존재하면 패스워드 검증- 패스워드가 맞다면
/etc/passwd
파일의 마지막에 설정되어있는 SHELL 변수에는 로그인 쉘을, USER와 USERNAME 변수에는 로그인 이름을 할당 - 로그인이 끝나면
/etc/passwd
파일에서 유저 라인의 마지막 단계에 입력되어 있는 프로그램을 실행. 일반적으로 배시 쉘로 설정되어 있음
쉘 초기화 파일들
/etc/profile
- 시스템 전역 쉘 변수 초기화
- 유저가 쉘에 로그인하면 가장 먼저 이 곳의 파일을 읽음. 이 파일에는 PATH, USER, LOGNAME, MAIL, HOSTNAME, HISTSIZE, INPUTRC 등의 쉘 변수들이 선언되어 있음
- 전역 리드라인 초기화 파일인
/etc/inputrc
파일을 읽어들이도록 되어 있으며, 프로그램들의 전역 환경을 설정하는 파일을 포함하고 있는/etc/profile.d
디렉터리를 읽어들이도록 구성되어 있음
/etc/bashrc
- 쉘 함수와 Alias를 위한 시스템 전역 변수 정의
~/.bash_profile
- 유저 개인의 환경 설정 파일
- 시스템 전역이 아닌 유저 자신만의 PATH와 시작 프로그램을 추가적으로 설정
~/.bashrc
- 유저 개인의 Alias 및 변수 설정 파일
- 유저 자신의 개인적 명령어 앨리아스를 정의할 수 있으며,
/etc/bashrc
파일에서 시스템 전역 변수를 읽은 다음, 특별한 프로그램을 위한 변수를 설정할 수 있음
~/.bash_logout
- 로그아웃 설정 파일
- 로그아웃 절차를 포함하고 있음. ex) 로그아웃하면 터미널 윈도우가 사라짐
source
명령- 쉘 환경 설정 파일 즉시 적용하기.
.
명령도 같은 기능을 수행
- 쉘 환경 설정 파일 즉시 적용하기.
- zsh의 경우
~/.zshrc
에 설정 파일이 존재!
명령 라인 파싱
- 히스토리 치환
- 명령라인은 토큰 또는 단어 단위
- 히스토리 업데이트
- 인용 진행
- 앨리아스 치환 / 함수 정의
- 리다이렉션, 백그라운드, 파이프 설정
- 변수 치환이 수행
- 명령 치환이 수행
- globbing 이라는 파일명 치환이 수행
- 명령이 실행
명령어 타입
- 앨리아스
- 키워드
- 함수
- 빌트인 내장명령
- 실행 파일
프로세스와 쉘
- 프로세스 : 유일한 PID 번호에 의해 식별될 수 있는 실행 프로그램
- 실행 프로그램의 데이터와 스택, 프로그램 포인터와 스택 포인터 그리고 프로그램을 실행하기 위해 필요한 모든 정보들로 구성
- 쉘 : 로그인 프로세스를 완료했을 때, 시작하는 특별한 프로그램으로 프로세스임
- 커널 : 프로세스를 제어하고 관리
시스템 콜
- 쉘 스크립트로부터 명령을 실행했을 때, 빌트인 내부 코드 / 디스크 저장장치에서 명령을 찾고 실행된 명령을 정렬
커널 영역의 기능을 사용자 모드가 사용 가능하게, 즉 프로세스가 하드웨어에 직접 접근해서 필요한 기능을 사용할 수 있게 해줌
- fork 시스템 콜 : 콜프로세스의 복사본을 생성
- 자식 프로세스는 fork가 호출된 다음 실행하고 두 프로세스는 CPU 공유. 자식 프로세스는 부모 프로세스의 환경, 오픈된 파일, 실제적 유저 ID, umask, 현재 작업 디렉터리, 시그널의 복사본을 가짐
- 명령을 실행했을 때 아래와 같은 Flow를 가짐
- 쉘은 명령 라인을 파싱하고 첫 단어가 빌트인 명령인지, 디스크에 존재하는 실행 명령인지 판단
- 빌트인 명령 -> 곧바로 처리
- 디스크에 존재 -> 부모 쉘의 복사본을 만들기 위해 fork 시스템 콜 호출
- 자식 프로세스는 명령을 찾기 위해 PATH에 정의되어 있는 경로 검색하고 리다이렉션, 파이프, 명령 치환, 백그라운드 프로세싱을 위한 파일 디스크립터를 설정
- 자식 프로세스가 동작하는 동안 부모 프로세스는 잠시 멈춤
- wait 시스템 콜 : 자식 프로 세스 하나가 종료될 때까지 부모 프로세스를 대기상태로 유지
- wait가 성공하면 자식 프로세스가 종료되고, 종료상태를 가지고 있는 자식 프로세스의 PID를 리턴
- 자식 프로세스가 종료되기 전 부모 프로세스가 종료된다면, init 프로세스는 자식 프로세스를 고아가 된 좀비 프로세스로 만듬
- 부모를 대기상태로 만드는 것 + 프로세스가 정상적으로 종료하도록 보증하는 역할
- exec 시스템 콜 : 터미널에 명령을 수행한 다음, 쉘은 새로운 쉘 프로세스를 fork. 자식 쉘은 타이핑된 명령을 수행
- 쉘은 프로그램을 위해 PATH를 검색 -> 프로그램이 발견되면 명령의 이름과 함께 exec 시스템 콜을 호출
- 커널은 이 프로그램을 메모리에 로드하며 자식 쉘은 새 프로그램으로 오버랩
- 새 프로그램은 자식 프로세스가 되고 실행을 시작(모든 환경 변수, 오픈 파일, 유저 정보를 물려받음)
- exit 시스템 콜 : 중지
- 자식 프로세스를 종료할 때 자식 프로세스는
sigchild
시그널을 보내고 자식의 종료상태를 부모 프로세스가 받아들이도록 대기. 종료상태는 0~255의 숫자이며 0은 성공적으로 실행되었단 뜻이며 0이 아닌 종료상태는 프로그램 실행리 실패했다는 뜻
- 자식 프로세스를 종료할 때 자식 프로세스는
- 프로세스 종료하기
CTRL-C
또는CTRL-\
키를 이용해 종료할 수 있으며,kill
명령을 사용해 종료할 수 있음.
변수
변수 타입
- 쉘 변수들은 대문자로 정의하며 2가지의 변수 타입을 가지고 있음
- 전역 변수
- 전역 변수 또는 환경 변수들은 모든 쉘에서 사용할 수 있으며,
env
명령을 사용하면 환경 변수들을 출력해볼 수 있음 echo $PATH
:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
이런식으로 나오는데 쉘은 명령어를 찾을 때마다 이 디렉토리를 순서대로 검사함- 추가하고 싶으면
vi ~/.bash_profile
에서export PATH=$PATH:/path/to/add
이런식으로 추가
- 전역 변수 또는 환경 변수들은 모든 쉘에서 사용할 수 있으며,
- 지역 변수
- 지역 변수는 현재의 쉘에서만 사용할 수 있음. 옵션 없이
set
빌트인 명령을 사용하면 환경 변수를 포함해 모든 변수들과 함수들의 목록을 보여줌
- 지역 변수는 현재의 쉘에서만 사용할 수 있음. 옵션 없이
- 다음 변수는 배시 쉘 프로그래밍 부분에서 나올 예정 (문자열 변수, 정수형 변수, 상수형 변수, 배열 변수)
변수 생성
- 변수들은 기본적으로 대문자로 생성. 숫자를 포함할 순 있지만 숫자로 시작할 수 없다
=
앞뒤로 공백이 없어야 한다.VAR="value"
이런식!- 변수를 출력할 경우엔
echo
를 사용.echo $VAR
,echo "$VAR"
,echo "${VAR}"
모두 동일한 기능 수행 - ★★★ 큰따옴표(““)를 쓰면
$str
의 실제 값을 출력할 수 있고(변수의 값) 작은따옴표(‘‘)를 쓰면 그냥$str
이란 문자열 자체를 출력함! - 밖에 큰따옴표를 쓰고 작은따옴표는 안에 쓰는 경우가 많음
지역 변수를 환경 변수로 만들기
- 현재 쉘에 만든 변수들은 현재 쉘에서만 사용할 수 있음. 이 경우
export
빌트인 명령을 사용하면 지역 변수를 환경 변수로 만들 수 있음 - 해제는
unset
- 자식 쉘에서 export 명령을 사용한다고 해서 부모 쉘에 영향을 주지는 못함
특수 파라미터 변수들 (Vㅔ리 중요)
$*
: 1부터 시작하는 위치 파라미터의 확장, 큰 따옴표로 구분하고 IFS 특수 변수의 첫 문자로 구분되는 각 파라미터의 값으로 하나의 단어를 확장하며, 전체 파라미터값을 가지고 있다. 만약 IFS가 NULL이거나 해제되어 잇으면 파라미터는 스페이스로 구분$@
: 1부터 시작하는 위치 파라미터의 확장이며, 큰 따옴표로 확장되면 각 파라미터는 하나의 구분 단어로 확장되고, 전체 파라미터값을 가짐$#
: 십진수의 위치 파라미터 전체 개수를 의미$?
: 최근 실행된 포그라운드 파이프라인의 종료상태를 가지고 있음(0은 정상적으로 종료)$-
: 실행하자마자 set 내장명령을 통해 또는 쉘 자체에 의해 설정된 현재 옵션 플래그로 확장$$
: 현재 쉘의 프로세스 ID$!
: 가장 최근에 백그라운드로 실행된 프로세스의 ID를 가지고 있음$0
:쉘 또는 쉘 스크립트의 이름을 가지고 있음$_
: 아규먼트 목록을 사용해 실행된 쉘스크립트의 절대 경로를 가지고 있음
본 쉘
- 본 쉘 shbang 라인 : 쉘 스크립트의 첫 라인에
#!/bin/sh
를 입력해서 커널에게 이 파일은 본 쉘 스크립트라고 인지해야 함 - 주석은
#
을 사용 - 와일드 카드 :
*
,?
,[]
는 파일명 확장을 위해 사용되고,<
,>
,2>
,>>
,|
문자들은 표준 IO를 위해 사용된다. 문자들이 인터프리터에 의해 해석되지 않도록 하기 위해선 인용부호('
,''
)로 감싸주면 됨 read
: 사용자의 입력을 읽고 read 명령의 오른쪽에 적는 변수에 사용자 입력값을 할당read word echo "입력한 단어는: $word"
- 조건문
if~then
문장을 사용하며 if문의 끝에는 반드시fi
를 적어 if문이 끝났음을 표시
if [표현식] then 명령 문장 블록 elif [표현식] then 명령 문장 블록 else 명령 문장 블록 fi
- case문
case "$color" in blue) echo $color is blue ;; green) echo $color is green ;; red|orange) echo $color is red or orange ;; *) echo "Not a color" # default esac
- 루프문 :
while
,until
,for
3가지 종류가 존재while
: 뒤에 오는 명령이나 표현식이 treu면 계속해서 do와 done 사이 문장 실행until
: while와 비슷하지만 until 뒤에 오는 명령이 false가 될 때까지 do와 done 사이 문장 실행. 거의 사용하지 않음for
: 단어목록을 통해서 반복, for 다음으로 변수 이름이 오고 in 다음 단어 목록이 온다. 그리고 do와 done 키워드 사이의 문장을 수행
- 파일 테스팅 :
test
은 상태 표현식을 판단하기 위해 사용. 파일, 디렉터리, plain 텍스트, 읽을 수 있는 파일 등의 속성을 테스트하기 위해 옵션을 가지고 있음-d
: 디렉터리인지 테스트-f
: 파일인지 테스트-r
: 현재 사용자가 읽을 수 있는지 테스트-s
: 파일 크기가 0보다 큰지 테스트-w
: 현재 사용자가 파일에 쓰기 가능한지 테스트-x
: 현재 사용자가 파일을 실행할 수 있는지 테스트
#!/bin/sh if [ -f file ] then echo "file exists" else echo "file not found" fi if [ -d file ] then echo "file is a directory" else echo "file is not a directory" fi if [ -s file ] then echo "file is not of zero length" else echo "file size is zero length" fi if [ -r file -a -w file ] then echo "file is readable and writable" else echo "file is not read/write" fi
함수
#!/bin/sh lister(){ echo "현재 디렉터리는 'pwd'입니다" echo "현재 디렉터리 파일은 아래와 같습니다" ls } lister
배쉬 쉘 문법과 구조
- 대부분의 리눅스에서 기본 쉘로 bash(Bourne Again SHell) 쉘을 사용하고 있음
- 로그인 과정
- init 프로세스 생성
- getty 프로세스 생성
- 로그인 프롬포트를 보여줌
- /bin/login 프로그램이 실행되고 로그인 아이디를 입력하면 로그인 패스워드 입력을 기다림
- 입력받은 패스워드가 정확하면 환경 설정이 초기화되고 로그인 쉘을 시작
- /etc/profile 시스템 파일을 찾아 명령 라인에서 실행
- 유저의 홈디렉터리에 있는 유저의 초기 파일인
.bash_profile
파일 실행,.bashrc
라는 환경 파일을 실행 - 달러($) 기호를 모니터에 보여주며 유저의 명령을 기다림
배시 쉘 옵션 설정을 위한 set
, shopt
명령
set
명령어는 특수한 빌트인 내장 옵션들을 켜고, 끄는 역할을 함set -o
: 쉘의 설정값 출력set -o option
: 옵션을 사용,set +o option
: 옵션을 사용하지 않음set -[a-z]
: 옵션을 사용하는 단축형set +[a-z]
: 옵션을 사용하지 않는 단축형- allexport
- braceexpand
- emacs
- errexit
- errtrace
- functrace
- hashall
- histexpand
- history
- ignoreeof
- interactive-comments
- keyword
- monitor
- noclobber
- noexec
- noglob
- nolog
- notify : 백그라운드 job이 종료되었을 때 유저에게 알려줌
- nounset
- onecmd
- physical
- pipefail
- posix
- privileged
- verbose
- vi
- xtrace
shopt
명령은 배시 쉘에서 사용되는 set 명령으로 새로운 버전의 쉘 옵션 내장명령어.-p
옵션을 사용해 모든 옵션을 출력해볼 수 있으며,-u
옵션을 사용해 옵션 셜정을off
할 수 있고,-s
옵션을 사용해on
할 수 있음
배열
- 배시 쉘은 1차원 배열 생성을 지원. 배열은 하나의 변수 이름에 숫자 목록, 이름 목록, 파일 목록 등의 단어 목록 집합을 할당할 수 있음. 빌트인 함수
declear -
로x[0]
형태로 생성 - 배열의 요소를 가져올 때에는
${배열명[index]}
형식을 사용하며-ar
옵션을 사용하면 읽기전용 배열이 생성 - 배열 요소의 개수를 파악할 경우엔
${#names[*]}
을 사용 - 배열의 인덱스를 사용해 특정 인덱스에만 값을 넣을 수 있음
함수
- 쉘의 컨텍스트 안에 명령들의 그룹 이름을 사용한다(자식 프로세스가 생성되지 않음)
- 함수가 한번 정의되면 쉘 메모리에 적재되기 때문에 함수가 호출될 때 디스크로부터 읽어들일 필요가 없다. 함수는 스크립트의 모듈을 향상시키기 위해 사용
실행될 때 프롬프트에서 정의될 수 있지만 대부분 유저의 초기화 파일(
.bash_profile
)에 정의한다. 그리고 함수는 호출되기 이전에 반드시 정의되야 함- 함수 정의
- 본 쉘 방식 : 함수명을 적고 빈 괄호를 적은 다음 함수 정의
function
키워드를 사용. 컬리 브레이스({
) 앞뒤에는 반드시 공백이 있어야 함- 정의 부분은 세미콜론(
;
)으로 분리된 명령어들로 구성되며, 마지막 명령어는 세미 콜론(;
)으로 끝나야 함
declare -F
명령을 실행하면 정의되어 있는 함수명을 출력할 수 있으며,unset -f 함수명
을 사용해 현재 쉘에서 정의한 함수를 제거할 수 있다
exec 명령
- 새로운 프로세스를 시작하지 않고 현재 프로그램을 대신하여 사용될 수 있다. 표준 출력 또는 표준 입력은 서브쉘을 생성하지 않고
exec
명령으로 변경될 수 있다. 만약 파일이exec
명령으로 오픈되면 파일의 끝까지 한 라인씩 읽게 된다
파이프
- 파이프 심볼(
|
)의 왼쪽 명령의 결과를 가져와서 오른쪽 명령의 입력으로 사용하도록 함 who | wc -l
- 파이프는 3단계의 처리를 수행
who
명령의 결과값이 커널 버퍼로 보내지고,wc -l
명령은 버퍼로부터 데이터를 읽은 다음, 결과를 모니터 스크린으로 보냄
카일스쿨 유튜브 채널을 만들었습니다. 데이터 사이언스, 성장, 리더십, BigQuery 등을 이야기할 예정이니, 관심 있으시면 구독 부탁드립니다 :)
PM을 위한 데이터 리터러시 강의를 만들었습니다. 문제 정의, 지표, 실험 설계, 문화 만들기, 로그 설계, 회고 등을 담은 강의입니다
이 글이 도움이 되셨거나 다양한 의견이 있다면 댓글 부탁드립니다 :)