
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와 유사하게 포맷 문자열을 사용하여 출력합니다.
# 기호로 시작하는 줄은 주석으로 처리되어 실행되지 않습니다. 코드 설명이나 임시 코드 비활성화에 사용됩니다.
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"${변수} | 변수 값 | 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" done13.2. while 루프
count=0 while [ $count -lt 5 ]; do echo "$count" ((count++)) # 산술 연산 done13.3. until 루프
count=5 until [ $count -le 0 ]; do echo "$count" ((count--)) done14. 조건문 (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" fi15. 선택문 (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)을 받았을 때 실행할 명령어를 지정합니다. (고급)
마무리
- Bash는 공백에 민감합니다. 명령어, 변수, 연산자 사이의 공백을 정확하게 사용해야 합니다.
- 변수를 사용할 때는 ${변수} 형태로 사용하는 것이 좋습니다. 특히 매개변수 확장을 사용할 때는 필수입니다.
- [ ] (test) 보다 [[ ]] (확장 test)를 사용하는 것이 좋습니다. [[ ]]는 더 많은 기능(패턴 매칭, &&, || 연산자 등)을 제공하고, [ ]보다 덜 혼란스럽습니다.
- 산술 연산은 $(( ))을 사용하는 것이 가독성이 좋습니다.
참고 자료
- 고급 Bash 스크립팅 가이드 (한글)
- Bash Guide for Beginners (영문)
- Bash Reference Manual (영문, 공식 문서)
- Shebang과 env에 대한 설명 (#!/usr/bin/env) (한글)
- Bash FAQ (영문)
- ShellCheck (온라인 쉘 스크립트 검사기)