Bourne Shell 스크립트 정리
≡ 목차 (Table of Contents)
이 글은 Bourne Shell 스크립트 문법 및 팁에 대해 전반적인 기준 없이 그냥 정리하는 글이다. 중구난방이고 개인적인 필요에 의해 업데이트 된다는 점에 유의하자.
셸 스크립트 실행하기
일반적으로 셸 스크립트를 만들면 셸을 이용해 실행하는 것이 보통이다.
sh ./somescript.sh
이 외에도 여러 실행 방법이 있다. 예를 들어
source somescript.sh
이렇게 하거나 혹은
. ./somescript.sh
이렇게 해도 스크립트가 실행된다.
스크립트를 실행 가능하게 만들기
만약 스크립트를 별도의 명령 없이 직접 파일 이름으로 실행되게 하고자 한다면 스크립트 제일 상단에 아래와 같이 실행 정보를 표기하는 주석문을 작성하자.
#!/bin/sh
#
으로 시작되는 구문은 원래 주석이기 때문에 실행 자체에 영향을 끼치진 않는다. 다만 유닉스에서 유래한 OS에서 스크립트의 첫 줄의 #!
은 해당 스크립트를 실행시키는 인터프리터나 셸의 경로를 적는 공간이다.
이제 실행 퍼미션을 주자. 예제는 스크립트 파일 이름이 somescript.sh
라고 가정한다.
chmod a+x somescript.sh
이렇게 하면 이 파일은 모든 사용자에게 실행(x) 권한이 주어진다.
이제 아래와 같은 명령으로 실행할 수 있다.
./somescript.sh
이런 식으로 실행시킬 셸을 명시해서 스크립트가 바로 실행되게 만들 수 있다.
참고로 이런 스크립트를 실행시키는 바이너리 정보 기입 법은 보통은 아래와 같은 스타일을 추천한다.
#!/usr/bin/env sh
env
라는 도구는 매개변수로 넘긴 도구가 어디에 있는지 찾아서 그 경로를 치환 시키는 도구다. 물론 sh가 아닌 bash
같은 셸을 명시해도 잘 동작해야 정상이다.
이후 PATH 환경 변수에 등록된 디렉터리로 옮기거나 해서 좀 더 쉽게 실행시킬 수 있게도 할 수 있다. 필요 없다면 확장자 .sh
를 빼버려도 유닉스나 리눅스, macOS 등에선 실행에 아무런 문제가 없다.
변수
변수를 생성하는 것은 아래와 같이 비교적 흔한(?) 방식으로 가능하다.
VAR_NAME="value"
=
사이에 공백이 있으면 안 된다. 공백이 들어가면 비교문으로 동작하기 때문이다.
환경변수로 뽑고 싶으면 export
를 붙이면 된다.
export VAR_NAME="value"
if문 정리
조건을 통해 분기를 정하는 스크립트에서 가장 중요한 부분이라고 볼 수 있는 문법이다. 대충 아래와 같은 식으로 사용한다.
if [ condition ] then ... elif [ condition ] then ... else ... fi
라인 수를 줄이길 원할 때는 세미콜론을 잘 쓰면 된다.
if [ condition ]; then foo bar fi if [ condition ] ; then foo bar; fi;
조건을 반대로 뒤집을 때(NOT)는 조건 앞에 !
을 쓰면 된다.
if [ ! condition ]; then foo bar; fi;
AND 혹은 OR 조건은 몇 가지 방법이 있지만 아래의 예 하나만 보자.
if { [ condition1 ] && [ condition2 ] ;} || [ condition3 ] ; then ... fi
위 코드는 AND 체크하는 두 식을 중괄호를 이용해 묶은 형태다. 즉 기성 언어라면 (c1 && c2) || c3
이렇게 표현했을 식이라고 볼 수 있다. 세미콜론의 위치를 잘 보자.
루프(Loop)
for
문을 이용한 현대적인(?) 루프는 아래처럼 쓸 수 있다.
VALUES="a\nb\nc" for VALUE in $VALUES; do echo $VALUE done
VALUES
라는 변수의 값은 세 줄로 이루어진 문자열 데이터다. \n
은 개행문자다. 그리고 위 예제의 for
문은 각 라인마다 한 줄 씩 동작한다. 즉 출력은 a, b, c가 차례대로 각 한 줄 마다 표시된다.
그리고 C 언어 방식의 for 루프를 비슷하게 구현할 수도 있다.
for (( i=0; i < 10; i++ )) do echo "$i" done
위의 코드는 0에서 9까지의 숫자를 콘솔에 표시한다.
조건식 오퍼레이터
조건문에 사용하는 몇 가지 논리 오퍼레이터를 정리해보자.
=
,!=
- 문자열이 동일한지, 동일하지 않은지
-eq
,-ne
,-gt
,-ge
,-lt
,-le
- 차례대로 equal, not equal, greater than, greater than or equal, less than, less than or equal
-a
,-o
- AND와 OR
-z
,-n
- 차례대로 문자열의 길이가 0(zero), 반대로 그 이상(not null)
-d
,-e
,-f
- 디렉토리가 존재하는지, 파일이 존재하는지, 문자열이 파일인지
-L
,-r
,-w
- 링크인지, 읽기 가능한지, 쓰기 가능한지
-s
- 파일 크기가 0 이상인지
-x
- 파일이 실행 가능한지
-nt
,-ot
,-ef
- 왼쪽 파일이 오른쪽 파일보다 최신인지, 과거인지, 같은 파일인지
디렉토리 존재 유무 체크
위에서도 정리했지만 -d
플래그를 쓸 수 있다.
if [ -d "/foo/bar/" ] ; then ... fi
반대로 파일 여부를 파악하려면 -e
혹은 -f
플래그를 쓰면 될 것 같다.
문자열 비교
if [ "$FOO" = "BAR" ]; then ... fi
=
사이의 공백이 꼭 필요하다. 공백이 없는 코드는 변수를 생성하는 코드이기 때문이다.
셸 스크립트에서 파일 개수 구하기
특정 디렉터리 안의 파일 갯수를 구하는 것은 바로는 안 되고 각 유틸리티를 이용한 결과를 얻어서 사용한다.
COUNT=$(ls /foo/bar | wc -l)
참고로 위 코드는 백쿼트(back-quote) `
를 사용하는 것과 비슷한 방식의 커맨드다. 즉 ls /foo/bar | wc -l
커맨드의 실행 결과가 위 변수에 주입된다.
위 결과를 이용해 파일이 하나 이상 존재하는지 체크하려면
if [ "$COUNT" -gt 0 ] ; then # 파일이 하나 이상 존재하는 경우 fi
이런 식으로 쓸 수 있다.
스크립트 실행 파라미터(Parameter)
실행 파라미터(Parameter), 프로그래밍 언어에서는 매개변수라고 부르지만 여기서는 실행 옵션이라는 의미가 더 와닿는 것 같다. 스크립팅시에는 대충 아래의 키워드가 쓰인다.
$#
- 파라미터 개수
$@
- 파라미터 전체
$0
- 실행 명령 이름, 즉 스크립트 파일 이름
$1
- 첫 번째 파라미터
$2
- 두 번째 파라미터 (이런 식으로 계속 숫자를 불리면 된다)
위의 방법과 조합해서 리스트 형태로 받아서 사용하는 방법도 있다.
args=("$@") for (( i=0; i < $#; i++ )) do param=${args[$i]} echo "$i parameter is $param" done
args
를 처리하는 방법을 잘 보자.
와일드카드 파라미터 처리
셸 스크립트에 옵션으로 *.ext
같은 파일 이름 와일드카드를 넘겼을 때 이를 나열하는 방법을 for 커맨드로 표현하면 이렇다.
for arg in "$@" do echo $arg ... done
앞서 살펴본 모든 파라미터를 받아오는 $@
와 for 루프를 이용한 트릭이다.
이 경우 arg
에는 각 파일의 경로가 들어있다. 즉 와일드카드에 해당하는 모든 파일의 경로를 for 루프를 통해 하나하나 얻을 수 있다. 각 파일을 가공하고 싶을 때 유용하다.
셸 스크립트 동작에 에러가 나면 멈추게 하기
셸 스크립트 상단에 아래 한 줄을 넣어두면 에러가 났을 때 스크립트가 멈춘다.
set -e
스크립트에서 실행하는 커맨드의 에러 메시지 숨기기
가끔 스크립트에서 여러 명령을 사용하는데 이 명령이 에러를 표시하는 게 보기 싫을 수도 있다. 이럴 때 에러 메시지는 stderr
(2) 파이프로 전달되니 이것을 지옥의 쓰레기통(?)에 던지면 된다.
some_shell_command 2> /dev/null
여기서 2>
이 표현이 에러를 어디로 리다이렉트(redirect) 시킬지 의미하는 표현이다.