
Bash는 리눅스 및 macOS와 같은 유닉스 계열 운영 체제의 기본 쉘(Shell)입니다. Bash 스크립트를 사용하면 반복적인 작업을 자동화하고, 시스템 관리 작업을 효율적으로 수행할 수 있습니다. 이 가이드에서는 Bash 입문자를 위해 기본적인 문법과 유용한 팁들을 예제와 함께 간단하게 정리했습니다. “이런 것도 있구나” 정도로 가볍게 이해하고 넘어가세요.
1. 첫 시작: Hello World 출력
고전적인 “Hello World” 출력부터 시작해봅시다. hello.sh
파일을 만들고 아래 코드를 작성하세요.
#!/usr/bin/env bash
echo "Hello, world!" # 출력: Hello, world!
printf "Hello, world!\n" # 줄바꿈을 명시적으로 추가. 출력: Hello, world!
printf "%s %s\n" hello world # C 스타일 포맷팅. 출력: hello world
실행 방법:
- 터미널에서
chmod +x hello.sh
명령어로 실행 권한을 부여합니다. (또는chmod 700 hello.sh
는 소유자에게만 실행 권한 부여) ./hello.sh
명령어로 스크립트를 실행합니다.
설명:
#!/usr/bin/env bash
: 스크립트 실행에 사용할 Bash 인터프리터 경로를 지정합니다. (Shebang, 셔뱅)echo
: 문자열을 출력하고 자동으로 줄바꿈합니다.printf
: C 언어의printf
와 유사하게 포맷 문자열을 사용하여 출력합니다.
2. 주석 (Comments)
#
기호로 시작하는 줄은 주석으로 처리되어 실행되지 않습니다. 코드 설명이나 임시 코드 비활성화에 사용됩니다.
3. 함수 (Function)
# 'function' 키워드는 생략 가능
my_function() {
echo "함수 호출됨"
echo "전달된 인자: $@"
}
# 함수 호출 (정의보다 뒤에 위치해야 함)
my_function
my_function "arg1" "arg2" # 인자 전달. 출력: arg1 arg2
설명:
- 함수 정의는
function 함수명() { ... }
또는함수명() { ... }
형식으로 합니다. - 함수 호출은 함수 이름을 사용합니다.
- 함수 호출 코드는 함수 정의보다 뒤에 있어야 합니다.
$@
: 함수에 전달된 모든 인자를 나타냅니다.
4. 변수 (Variable)
# 전역 변수
global_var="Hello"
my_function() {
# 지역 변수
local local_var="World" # local 변수는 함수 내에서만 유효. 함수 밖의 같은 이름의 변수에 영향 X
echo "지역 변수: $local_var" # 출력: 지역 변수: World
global_var="Modified Global" # 전역 변수 수정
}
my_function()
echo "전역 변수: $global_var" # 출력: 전역 변수: Modified Global
# 환경 변수 (자식 스크립트에서도 사용 가능)
export MY_ENV_VAR="Environment Value"
# 자식 스크립트 실행 예시 (child.sh)
# ./child.sh # child.sh 스크립트 안에서 echo $MY_ENV_VAR 실행
설명:
- 변수 선언/할당 시
=
앞뒤에 공백이 없어야 합니다. - 변수는 기본적으로 전역 변수입니다.
- 함수 내에서
local
키워드를 사용하여 지역 변수를 선언할 수 있습니다. export
를 사용하여 환경 변수를 설정하면, 현재 스크립트뿐만 아니라 자식 스크립트에서도 사용할 수 있습니다.- 예약 변수(Reserved Variable) 사용 시 주의해야 합니다. (예:
HOME
,PATH
,USER
등). 환경 변수는 주로.bash_profile
,.bashrc
, 또는 시스템 설정 파일에서 정의합니다.
5. 예약 변수 (Reserved Variable)
Bash에는 미리 정의된 예약 변수들이 있습니다. 자주 사용되는 예약 변수들은 다음과 같습니다.
변수 | 설명 |
---|---|
HOME |
사용자의 홈 디렉토리 |
PATH |
실행 파일을 찾을 경로 |
PWD |
현재 작업 디렉토리 |
USER |
현재 사용자 이름 |
UID |
현재 사용자 ID |
SHELL |
현재 사용 중인 쉘 경로 |
FUNCNAME |
현재 함수 이름 (함수 내부에서 사용) |
SECONDS |
스크립트 실행 시간 (초) |
RANDOM |
랜덤 정수 (0-32767) |
$$ |
현재 스크립트의 프로세스 ID (PID) |
$? |
마지막으로 실행한 명령어의 종료 상태 (0: 성공, 0 이외: 실패) |
$! |
마지막으로 실행한 백그라운드 프로세스의 PID |
더 많은 예약 변수는 Bash Reference Manual을 참고하세요.
6. 위치 매개변수 (Positional Parameters)
스크립트나 함수에 전달된 인자(arguments)를 참조하는 데 사용됩니다.
변수 | 설명 |
---|---|
$0 |
실행된 스크립트 이름 |
$1 , $2 , … |
첫 번째, 두 번째, … 인자. 10번째 이상은 ${10} , ${11} 처럼 중괄호 사용 |
$* |
모든 인자를 하나의 문자열로 합쳐서 표시 (“arg1 arg2 …”) |
$@ |
모든 인자를 각각의 개별적인 문자열로 표시 (“arg1” “arg2” …) |
$# |
전달된 인자의 개수 |
7. 특수 매개변수 (Special Parameters)
위에서 설명한 $$
, $?
, $!
외에도 다른 특수 매개변수들이 있습니다.
변수 | 설명 |
---|---|
$- |
현재 쉘에 설정된 옵션 플래그 |
$_ |
이전에 실행된 명령어의 마지막 인자 |
8. 매개변수 확장 (Parameter Expansion)
변수의 값을 다양하게 조작하고 활용하는 방법입니다.
string="abc-efg-123-abc"
표현식 | 설명 | 예시 (string="abc-efg-123-abc" ) |
---|---|---|
${변수} |
변수 값 | echo ${string} → abc-efg-123-abc |
${변수:위치} |
위치부터 끝까지 부분 문자열 | echo ${string:4} → efg-123-abc |
${변수:위치:길이} |
위치부터 길이만큼 부분 문자열 | echo ${string:4:3} → efg |
${#변수} |
문자열 길이 | echo ${#string} → 15 |
${변수:-단어} |
변수가 unset 또는 null이면 ‘단어’ 반환, 아니면 변수 값 | echo ${unset_var:-default} → default |
${변수-단어} |
변수가 unset이면 ‘단어’ 반환, 아니면 변수 값 | echo ${unset_var-default} → default |
${변수:=단어} |
변수가 unset 또는 null이면 ‘단어’ 할당 후 반환, 아니면 변수 값 | echo ${unset_var:=default}; echo $unset_var → default default |
${변수=단어} |
변수가 unset이면 ‘단어’ 할당 후 반환, 아니면 변수 값 | echo ${unset_var=default}; echo $unset_var → default default |
${변수:?메시지} |
변수가 unset 또는 null이면 메시지 출력 후 종료 | echo ${unset_var:?Error} → (스크립트 종료) Error |
${변수?메시지} |
변수가 unset이면 메시지 출력 후 종료 | echo ${unset_var?Error} → (스크립트 종료) Error |
${변수:+단어} |
변수가 설정되어 있으면 ‘단어’ 반환, 아니면 null | echo ${string:+exists} → exists |
${변수+단어} |
변수가 설정되어 있으면 ‘단어’ 반환, 아니면 null. ${변수:+단어} 와 동일. |
echo ${string+exists} → exists |
${변수#패턴} |
앞에서부터 가장 짧은 패턴 일치 제거 | echo ${string#*-} → efg-123-abc |
${변수##패턴} |
앞에서부터 가장 긴 패턴 일치 제거 | echo ${string##*-} → abc |
${변수%패턴} |
뒤에서부터 가장 짧은 패턴 일치 제거 | echo ${string%-*} → abc-efg-123 |
${변수%%패턴} |
뒤에서부터 가장 긴 패턴 일치 제거 | echo ${string%%-*} → abc |
${변수/패턴/대체} |
첫 번째 패턴 일치 대체 | echo ${string/abc/XYZ} → XYZ-efg-123-abc |
${변수//패턴/대체} |
모든 패턴 일치 대체 | echo ${string//abc/XYZ} → XYZ-efg-123-XYZ |
${변수/#패턴/대체} |
문자열 시작 부분에서 패턴 일치 대체 | echo ${string/#abc/XYZ} → XYZ-efg-123-abc |
${변수/%패턴/대체} |
문자열 끝 부분에서 패턴 일치 대체 | echo ${string/%abc/XYZ} → abc-efg-123-XYZ |
${!prefix*} 또는 ${!prefix@} |
prefix로 시작하는 모든 변수 이름 | (var1=1; var2=2;) echo ${!var*} → var1 var2 |
9. 배열 (Array)
# 배열 선언 (declare -a는 필수는 아님)
declare -a my_array
# 배열 초기화
my_array=("apple" "banana" "cherry")
# 인덱스를 사용한 접근
echo ${my_array[0]} # 첫 번째 요소 출력 (apple)
# 배열 전체 출력
echo ${my_array[@]} # 모든 요소 출력 (apple banana cherry)
# 배열 길이
echo ${#my_array[@]} # 요소 개수 출력 (3)
# 요소 추가
my_array+=("date") # 배열 끝에 요소 추가
my_array[4]="fig" # 특정 인덱스에 요소 추가 (중간 인덱스 비워도 됨)
# 배열 복사
new_array=("${my_array[@]}")
# 요소 삭제
unset my_array[1] # 두 번째 요소 삭제
unset my_array # 배열 전체 삭제
주의: Bash 배열은 0부터 시작하는 인덱스를 사용하며, 1차원 배열만 지원합니다.
10. 변수 타입 지정 (Variable Types)
Bash는 기본적으로 변수를 문자열로 취급하지만, declare
(또는 typeset
) 명령어를 사용하여 타입을 지정할 수 있습니다. 하지만 Bash의 타입 지정은 엄격하지 않습니다.
declare -i num=10 # 정수 (integer)
declare -r readonly_var="Readonly" # 읽기 전용 (readonly)
declare -a array_var # 배열 (array)
declare -x export_var="Exported" # 환경 변수 (export)
declare -f # 정의된 모든 함수 목록
declare -f function_name # 특정 함수 정의
-i
: 정수-r
: 읽기 전용(readonly)-a
: 배열-x
: 환경 변수(export)-f
: 함수
declare
를 명시적으로 사용하는 것보다 변수 할당 시 타입을 자연스럽게 지정하는 방식을 추천합니다. (예: num=10
(정수), my_array=("a" "b")
(배열))
11. 연산자 (Operators)
11.1. 논리 연산자 (Logical Operators)
연산자 | 설명 | 예시 |
---|---|---|
&& |
AND (그리고) | [ condition1 ] && [ condition2 ] |
|| |
OR (또는) | [ condition1 ] || [ condition2 ] |
! |
NOT (부정) | ! [ condition ] |
11.2. 산술 연산자 (Arithmetic Operators)
연산자 | 설명 |
---|---|
+ , - , * , / |
더하기, 빼기, 곱하기, 나누기 |
** |
거듭제곱 |
% |
나머지 |
+= , -= , *= , /= , %= |
복합 대입 연산자 |
11.3. 비트 연산자 (Bitwise Operators)
연산자 | 설명 |
---|---|
<< |
왼쪽 시프트 |
>> |
오른쪽 시프트 |
& |
비트 AND |
| |
비트 OR |
~ |
비트 NOT (단항) |
^ |
비트 XOR |
<<= , >>= , &= , |= , ^= |
복합 대입 연산자 (비트) |
11.4. 기타 연산자
연산자 | 설명 |
---|---|
, |
콤마 연산자: 여러 표현식을 순차적으로 실행 |
12. 비교 (Comparison)
12.1. 정수 비교 (Integer Comparison)
연산자 | 설명 | (( )) 또는 [[ ]] 에서 사용 |
[ ] 에서 사용 |
---|---|---|---|
-eq |
같음 (equal) | == 또는 = |
-eq |
-ne |
같지 않음 (not equal) | != |
-ne |
-gt |
큼 (greater than) | > |
-gt |
-ge |
크거나 같음 (greater than or equal) | >= |
-ge |
-lt |
작음 (less than) | < |
-lt |
-le |
작거나 같음 (less than or equal) | <= |
-le |
12.2. 문자열 비교 (String Comparison)
연산자 | 설명 | 예시 |
---|---|---|
= 또는 == |
같음 | [[ "$str1" == "$str2" ]] |
!= |
같지 않음 | [[ "$str1" != "$str2" ]] |
< |
사전순으로 앞섬 (ASCII) | [[ "$str1" < "$str2" ]] |
> |
사전순으로 뒤섬 (ASCII) | [[ "$str1" > "$str2" ]] |
-z |
문자열이 비어있음 (길이 0) | [ -z "$str" ] |
-n |
문자열이 비어있지 않음 | [ -n "$str" ] |
12.3. 파일 비교 (File Test Operators)
연산자 | 설명 |
---|---|
-e file |
파일이 존재함 |
-f file |
일반 파일 |
-d file |
디렉토리 |
-s file |
파일 크기가 0보다 큼 |
-r file |
읽기 가능 |
-w file |
쓰기 가능 |
-x file |
실행 가능 |
-h file 또는 -L file |
심볼릭 링크 |
file1 -nt file2 |
file1 이 file2 보다 최신 |
file1 -ot file2 |
file1 이 file2 보다 오래됨 |
file1 -ef file2 |
file1 과 file2 가 같은 파일을 가리킴 (하드 링크) |
더 많은 파일 비교 연산자는 man test
또는 help test
명령으로 확인할 수 있습니다.
13. 반복문 (Loops)
break
: 반복문 종료continue
: 다음 반복으로 건너뜀
13.1. for
루프
# 리스트 순회
for item in apple banana cherry; do
echo "$item"
done
# C 스타일 for 루프
for ((i = 0; i < 5; i++)); do
echo "$i"
done
# 파일 목록 순회 (ls 사용은 권장하지 않음)
for file in *.txt; do
echo "$file"
done
# find와 함께 사용 (더 안전한 방법)
find . -name "*.txt" -print0 | while IFS= read -r -d $'\0' file; do
echo "$file"
done
13.2. while
루프
count=0
while [ $count -lt 5 ]; do
echo "$count"
((count++)) # 산술 연산
done
13.3. until
루프
count=5
until [ $count -le 0 ]; do
echo "$count"
((count--))
done
14. 조건문 (Conditional Statements)
if [ "$str1" = "$str2" ]; then
echo "문자열이 같습니다."
elif [ "$str1" = "$str3" ]; then
echo "문자열이 같습니다."
else
echo "문자열이 다릅니다."
fi
# [[ ]] 사용 (더 많은 기능, 패턴 매칭 등)
if [[ "$str1" == a* ]]; then # $str1이 'a'로 시작하는지 확인
echo "Starts with a"
fi
# (( )) 사용 (산술 연산)
if (( num > 10 )); then
echo "Number is greater than 10"
fi
# 다중 조건
if [ "$str1" = "$str2" ] && [ "$num1" -gt "$num2" ]; then
echo "Both conditions are true"
fi
# [[ ]] 에서의 다중 조건 (더 깔끔)
if [[ "$str1" = "$str2" && $num1 -gt $num2 ]]; then
echo "Both conditions are true"
fi
주의: 조건문 내에 실행 문장이 없으면 오류가 발생합니다. 빈 블록을 만들려면 :
(null command)를 사용하세요.
if [ -z "$var" ]; then
: # Do nothing
else
echo "var is not empty"
fi
15. 선택문 (case
Statement)
case "$variable" in
pattern1)
# pattern1과 일치할 때 실행할 명령어
;;
pattern2 | pattern3)
# pattern2 또는 pattern3과 일치할 때 실행할 명령어
;;
*) # 어떤 패턴과도 일치하지 않을 때 (default)
# 기본 명령어
;;
esac
for str in HELLO WORLD hello world s start end etc; do
case "$str" in
hello|HELLO)
echo "$str: Matches 'hello' (case-insensitive)"
;;
wo*)
echo "$str: Starts with 'wo'"
;;
[sS]tart) # s 또는 S로 시작하는 start
echo "$str : Matches 'start' (case-insensitive)"
;;
e|end)
echo "$str: Matches 'e' or 'end'"
;;
*) # Default case
echo "$str: Other"
;;
esac
done
설명:
case
문은 변수 값을 여러 패턴과 비교하여 일치하는 경우 해당 블록의 명령어를 실행합니다.- 각 패턴 블록은
;;
로 끝나야 합니다. |
를 사용하여 여러 패턴을 OR 조건으로 묶을 수 있습니다.*
는 모든 경우에 일치하는 와일드카드 패턴입니다 (default case).- 패턴에는 와일드카드(
*
,?
,[...]
)를 사용할 수 있습니다. case
문 비교는 대소문자를 구분합니다.
16. 디버깅 (Debugging)
스크립트 실행 중 발생하는 문제를 찾고 해결하는 과정입니다.
echo
: 변수 값이나 특정 메시지를 출력하여 코드 실행 흐름을 추적합니다.set -x
(bash -x
): 명령어 실행 전에 해당 명령어를 출력합니다.set -v
(bash -v
): 명령어 실행 전에 코드를 그대로 출력합니다.set -u
: 선언되지 않은 변수를 사용하려고 할 때 오류 메시지를 출력하고 스크립트를 종료합니다.set -e
: 명령어가 실패(0이 아닌 종료 상태 반환)하면 스크립트를 즉시 종료합니다.bash -n script.sh
: 스크립트를 실행하지 않고 문법 오류만 검사합니다.trap
: 특정 시그널(signal)을 받았을 때 실행할 명령어를 지정합니다. (고급)
# 디버깅 예시
set -x # 명령어 추적 활성화
my_var="Hello"
echo "Value: $my_var"
set +x # 명령어 추적 비활성화
마무리
- Bash는 공백에 민감합니다. 명령어, 변수, 연산자 사이의 공백을 정확하게 사용해야 합니다.
- 변수를 사용할 때는
${변수}
형태로 사용하는 것이 좋습니다. 특히 매개변수 확장을 사용할 때는 필수입니다. [ ]
(test) 보다[[ ]]
(확장 test)를 사용하는 것이 좋습니다.[[ ]]
는 더 많은 기능(패턴 매칭,&&
,||
연산자 등)을 제공하고,[ ]
보다 덜 혼란스럽습니다.- 산술 연산은
$(( ))
을 사용하는 것이 가독성이 좋습니다.
참고 자료
- 고급 Bash 스크립팅 가이드 (한글)
- Bash Guide for Beginners (영문)
- Bash Reference Manual (영문, 공식 문서)
- Shebang과 env에 대한 설명 (#!/usr/bin/env) (한글)
- Bash FAQ (영문)
- ShellCheck (온라인 쉘 스크립트 검사기)