본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 11. 17. · 17 Views
Bash 조건문과 분기 처리 완벽 가이드
Linux/Bash 셸 스크립트에서 조건에 따라 다른 작업을 수행하는 방법을 배워보세요. if-then-else 구조부터 case 문, 다양한 테스트 연산자까지 실무에서 바로 활용할 수 있는 조건문 활용법을 소개합니다.
목차
1. if-then-else 구조
시작하며
여러분이 서버 관리 스크립트를 작성할 때 이런 상황을 겪어본 적 있나요? "디스크 용량이 80% 이상이면 경고 메시지를 보내고, 그렇지 않으면 정상이라고 표시하고 싶은데 어떻게 해야 하지?" 이런 문제는 실제 개발 현장에서 자주 발생합니다.
시스템 모니터링, 파일 존재 여부 확인, 사용자 입력 검증 등 프로그램이 상황에 따라 다른 행동을 해야 하는 경우가 매우 많기 때문입니다. 조건문이 없다면 모든 코드는 위에서 아래로 한 줄씩만 실행될 것이고, 프로그램은 똑똑한 판단을 할 수 없게 됩니다.
바로 이럴 때 필요한 것이 if-then-else 구조입니다. 이것은 마치 일상생활에서 "만약 비가 오면 우산을 챙기고, 그렇지 않으면 그냥 나간다"처럼 조건에 따라 다른 행동을 선택할 수 있게 해줍니다.
개요
간단히 말해서, if-then-else는 "만약 ~라면 이것을 해라, 그렇지 않으면 저것을 해라"라는 의미를 코드로 표현하는 방법입니다. Bash 스크립트에서는 자동화 작업을 수행할 때 수많은 분기가 필요합니다.
예를 들어, 백업 스크립트를 만들 때 "백업 디렉토리가 없으면 생성하고, 이미 있으면 기존 파일을 덮어쓸지 물어보는" 같은 경우에 매우 유용합니다. 전통적으로 프로그래밍을 배운 분들은 다른 언어의 if 문법에 익숙할 것입니다.
기존에는 중괄호 {}를 사용했다면, Bash에서는 then과 fi(if를 거꾸로 쓴 것)를 사용하여 조건문 블록을 표시합니다. if-then-else 구조의 핵심 특징은 세 가지입니다.
첫째, 조건식의 결과가 참(성공, exit code 0)이면 then 블록이 실행됩니다. 둘째, 거짓(실패, exit code 0이 아님)이면 else 블록이 실행됩니다.
셋째, elif를 사용하여 여러 조건을 연쇄적으로 검사할 수 있습니다. 이러한 특징들이 복잡한 로직을 단순하고 명확하게 표현할 수 있게 해주기 때문에 중요합니다.
코드 예제
#!/bin/bash
# 디스크 사용률 체크 스크립트
USAGE=$(df -h / | tail -1 | awk '{print $5}' | sed 's/%//')
if [ $USAGE -ge 80 ]; then
# 80% 이상이면 경고 메시지
echo "경고: 디스크 사용률이 ${USAGE}%입니다!"
echo "디스크 정리가 필요합니다."
elif [ $USAGE -ge 50 ]; then
# 50~79%면 주의 메시지
echo "주의: 디스크 사용률이 ${USAGE}%입니다."
else
# 50% 미만이면 정상
echo "정상: 디스크 사용률이 ${USAGE}%입니다."
fi
설명
이것이 하는 일: 이 스크립트는 루트 파티션(/)의 디스크 사용률을 확인하고, 사용률에 따라 다른 메시지를 출력합니다. 첫 번째로, df 명령어로 디스크 사용 정보를 가져와서 파이프(|)로 연결된 여러 명령어를 거쳐 숫자만 추출합니다.
tail -1은 마지막 줄만 가져오고, awk '{print $5}'는 다섯 번째 컬럼(사용률)을 추출하며, sed 's/%//'는 퍼센트 기호를 제거합니다. 이렇게 하는 이유는 숫자 비교를 위해서입니다.
그 다음으로, if [ $USAGE -ge 80 ]이 실행되면서 사용률이 80 이상인지 검사합니다. 만약 참이면 then 다음의 경고 메시지가 출력됩니다.
-ge는 "greater than or equal"의 약자로 "크거나 같음"을 의미합니다. 내부에서는 test 명령어가 두 숫자를 비교하여 성공(0) 또는 실패(1)의 exit code를 반환합니다.
elif (else if의 줄임말) 블록은 첫 번째 조건이 거짓일 때만 검사됩니다. 여기서는 50 이상인지 확인하여 "주의" 메시지를 출력합니다.
마지막으로, else 블록이 실행되어 50% 미만일 때 "정상" 메시지를 출력하고, fi로 if 문을 종료합니다. 여러분이 이 코드를 사용하면 서버 모니터링 자동화, 사용자 입력 검증, 파일 존재 여부에 따른 처리 등 다양한 상황에서 똑똑한 판단을 하는 스크립트를 만들 수 있습니다.
실무에서는 cron에 등록하여 주기적으로 실행하거나, 배포 스크립트에서 환경별 설정을 선택하는 등의 용도로 활용됩니다.
실전 팁
💡 if와 then을 한 줄에 쓰려면 세미콜론을 사용하세요: if [ 조건 ]; then. 이렇게 하면 코드가 더 간결해집니다.
💡 조건문 안에서 변수를 사용할 때는 반드시 쿼테이션으로 감싸세요: if [ "$VAR" = "value" ]. 변수가 비어있을 때 문법 오류를 방지할 수 있습니다.
💡 여러 조건을 검사할 때는 elif를 활용하세요. else 안에 또 다른 if를 중첩하는 것보다 가독성이 훨씬 좋습니다.
💡 디버깅할 때는 스크립트 상단에 set -x를 추가하면 각 명령어가 실행되기 전에 출력되어 어느 분기로 들어가는지 확인할 수 있습니다.
💡 복잡한 조건은 함수로 분리하세요. is_disk_full() 같은 함수를 만들면 if is_disk_full; then처럼 읽기 쉬운 코드를 작성할 수 있습니다.
2. test 명령어와 [ ] 연산자
시작하며
여러분이 스크립트를 작성하다가 이런 질문을 해본 적 있나요? "if 문 안에 있는 대괄호 [ ]는 도대체 무엇이고, 왜 앞뒤로 공백이 꼭 필요한 거지?" 이런 의문은 Bash 초보자들이 가장 많이 겪는 혼란입니다.
다른 프로그래밍 언어에서는 괄호가 단순히 그룹핑을 위한 문법적 요소인데, Bash에서는 [ ]가 실제로는 명령어라는 사실을 모르면 계속 문법 오류에 시달리게 됩니다. 바로 이럴 때 필요한 것이 test 명령어에 대한 이해입니다.
[ ]는 사실 test 명령어의 다른 표현일 뿐이며, 파일 존재 여부, 문자열 비교, 숫자 비교 등 다양한 조건을 검사할 수 있는 강력한 도구입니다.
개요
간단히 말해서, test 명령어는 조건을 평가하여 참이면 0(성공), 거짓이면 1(실패)을 반환하는 내장 명령어이고, [ ]는 test와 완전히 동일하게 작동하는 별칭입니다. Bash에서는 파일 시스템 작업을 할 때 조건 검사가 필수입니다.
예를 들어, "설정 파일이 존재하고 읽을 수 있는지 확인한 후에 파싱하기", "로그 파일 크기가 너무 크면 순환시키기" 같은 경우에 test 명령어가 핵심 역할을 합니다. 전통적인 방법에서는 명령어를 실행하고 $?로 exit code를 확인했다면, 이제는 test나 [ ]를 사용하여 한 줄로 간결하게 조건을 검사할 수 있습니다.
test 명령어의 핵심 특징은 세 가지입니다. 첫째, -f(파일), -d(디렉토리), -e(존재) 같은 파일 테스트 연산자를 제공합니다.
둘째, -eq, -ne, -lt, -gt 같은 숫자 비교 연산자를 지원합니다. 셋째, =, != 같은 문자열 비교도 가능합니다.
이러한 특징들이 시스템 관리 스크립트 작성을 매우 편리하게 만들어주기 때문에 중요합니다.
코드 예제
#!/bin/bash
# 백업 스크립트 - 파일 검사 예제
CONFIG_FILE="/etc/myapp/config.conf"
BACKUP_DIR="/backup"
# test 명령어 직접 사용
if test -f "$CONFIG_FILE"; then
echo "설정 파일이 존재합니다."
fi
# [ ] 연산자 사용 (test와 동일)
if [ -r "$CONFIG_FILE" ]; then
# -r: 읽기 권한 확인
echo "설정 파일을 읽을 수 있습니다."
source "$CONFIG_FILE"
fi
# 디렉토리 존재 여부 확인
if [ ! -d "$BACKUP_DIR" ]; then
# !는 부정 (not)을 의미
echo "백업 디렉토리가 없어서 생성합니다."
mkdir -p "$BACKUP_DIR"
fi
설명
이것이 하는 일: 이 스크립트는 설정 파일과 백업 디렉토리의 존재 여부와 권한을 확인하여 안전하게 백업을 준비합니다. 첫 번째로, test -f "$CONFIG_FILE"은 파일이 존재하고 일반 파일(디렉토리나 특수 파일이 아님)인지 검사합니다.
-f는 "file"의 약자로, 심볼릭 링크가 아닌 실제 파일을 찾습니다. 왜 이렇게 하는지는 설정 파일이 실제로 있어야 프로그램이 정상 작동하기 때문입니다.
그 다음으로, [ -r "$CONFIG_FILE" ]이 실행되면서 파일을 읽을 수 있는 권한이 있는지 검사합니다. -r은 "readable"의 약자입니다.
내부에서는 현재 사용자의 권한을 확인하여 읽기가 가능하면 0을, 불가능하면 1을 반환합니다. 읽기가 가능하면 source 명령으로 설정을 로드합니다.
세 번째 단계에서는 [ ! -d "$BACKUP_DIR" ]로 디렉토리가 없는지 확인합니다.
느낌표(!)는 조건을 반대로 만들어서, "디렉토리가 존재하지 않으면 참"이 됩니다. 마지막으로, 디렉토리가 없으면 mkdir -p로 상위 디렉토리까지 모두 생성하여 백업 준비를 완료합니다.
여러분이 이 코드를 사용하면 파일이나 디렉토리가 없어서 스크립트가 중간에 실패하는 것을 방지할 수 있습니다. 실무에서는 배포 스크립트에서 의존성 파일 확인, 로그 디렉토리 생성, 권한 검증 등에 활용됩니다.
특히 자동화된 작업에서 사전 검사를 철저히 하면 오류를 조기에 발견하여 안정성이 크게 향상됩니다.
실전 팁
💡 [ ]를 사용할 때는 반드시 여는 괄호와 닫는 괄호 안쪽에 공백을 넣으세요. [와 ]는 명령어 이름이므로 공백으로 인자를 구분해야 합니다.
💡 변수를 사용할 때는 항상 쿼테이션으로 감싸세요: [ -f "$FILE" ]. 변수가 비어있거나 공백이 포함되어 있을 때 오류를 방지합니다.
💡 파일 테스트 연산자를 조합할 때는 -a(and)나 -o(or)보다 &&와 ||를 사용하는 것이 권장됩니다: [ -f file ] && [ -r file ]
💡 test 명령어의 모든 연산자는 man test로 확인할 수 있습니다. -nt(newer than), -ot(older than) 같은 유용한 연산자들이 많습니다.
💡 숫자 비교는 -eq, -ne 등을 사용하고, 문자열 비교는 =, != 를 사용하세요. 혼동하면 예상치 못한 결과가 나올 수 있습니다.
3. [[ ]] 이중 대괄호 사용
시작하며
여러분이 스택오버플로우나 GitHub에서 Bash 스크립트를 볼 때 이런 궁금증을 가진 적 있나요? "어떤 스크립트는 [ ]를 쓰고, 어떤 스크립트는 [[ ]]를 쓰던데, 무슨 차이지?
그냥 똑같은 거 아닌가?" 이런 혼란은 Bash를 배우는 사람들이 겪는 매우 자연스러운 의문입니다. 겉으로 보기에는 대괄호가 하나 더 있는 것뿐이지만, 실제로는 [[ ]]가 훨씬 강력하고 안전한 기능을 제공합니다.
특히 문자열 처리나 패턴 매칭을 할 때 단일 대괄호의 한계를 명확히 느끼게 됩니다. 바로 이럴 때 필요한 것이 이중 대괄호 [[ ]]입니다.
이것은 Bash의 확장 기능으로, 더 직관적인 문법과 강력한 기능을 제공하여 현대적인 스크립트 작성의 표준이 되었습니다.
개요
간단히 말해서, [[ ]]는 Bash에서 제공하는 개선된 조건 테스트 키워드로, [ ]보다 안전하고 강력한 기능을 제공합니다. 실무에서는 복잡한 조건 검사가 필요한 경우가 많습니다.
예를 들어, "파일 이름이 특정 패턴과 일치하는지 확인하기", "문자열에 공백이 포함되어도 안전하게 비교하기", "정규표현식으로 입력값 검증하기" 같은 경우에 [[ ]]가 훨씬 편리합니다. 기존의 [ ]는 POSIX 표준을 따르는 오래된 방식으로 모든 유닉스 시스템에서 작동하지만, 변수 쿼테이션을 빠뜨리면 오류가 나고 패턴 매칭이 제한적입니다.
이제 [[ ]]를 사용하면 변수를 쿼테이션 없이 안전하게 사용할 수 있고, 와일드카드와 정규표현식을 직접 사용할 수 있습니다. [[ ]]의 핵심 특징은 네 가지입니다.
첫째, 변수를 쿼테이션 없이 사용해도 안전합니다. 둘째, ==로 패턴 매칭을 할 수 있고, =~로 정규표현식을 사용할 수 있습니다.
셋째, &&와 || 논리 연산자를 대괄호 안에서 직접 사용할 수 있습니다. 넷째, 단어 분리(word splitting)와 경로명 확장(pathname expansion)이 일어나지 않아 더 예측 가능합니다.
이러한 특징들이 코드를 더 안전하고 읽기 쉽게 만들어주기 때문에 중요합니다.
코드 예제
#!/bin/bash
# 사용자 입력 검증 스크립트
read -p "사용자 이름을 입력하세요: " username
# 패턴 매칭 - 알파벳으로 시작하는지 확인
if [[ $username == [a-zA-Z]* ]]; then
echo "올바른 형식입니다."
else
echo "사용자 이름은 알파벳으로 시작해야 합니다."
exit 1
fi
# 정규표현식 매칭 - 이메일 형식 검증
read -p "이메일을 입력하세요: " email
if [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "올바른 이메일 형식입니다."
else
echo "잘못된 이메일 형식입니다."
fi
# 복합 조건 - 논리 연산자 사용
if [[ -f config.txt && -r config.txt ]]; then
echo "설정 파일이 존재하고 읽을 수 있습니다."
fi
설명
이것이 하는 일: 이 스크립트는 사용자가 입력한 사용자 이름과 이메일이 올바른 형식인지 패턴 매칭과 정규표현식으로 검증합니다. 첫 번째로, [[ $username == [a-zA-Z]* ]]는 사용자 이름이 알파벳으로 시작하는지 확인합니다.
[a-zA-Z]*는 와일드카드 패턴으로, "알파벳 하나 뒤에 뭐가 와도 됨"을 의미합니다. 단일 대괄호에서는 이 패턴이 문자열 리터럴로 취급되지만, 이중 대괄호에서는 실제 패턴 매칭으로 작동합니다.
이렇게 하면 admin123은 통과하지만 123admin은 거부됩니다. 그 다음으로, [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$ ]]가 실행되면서 정규표현식으로 이메일 형식을 검증합니다.
=~ 연산자는 오른쪽의 정규표현식과 왼쪽의 문자열을 비교합니다. 내부에서는 ^(시작), $(끝), +(1개 이상), {2,}(2개 이상) 같은 정규표현식 메타문자가 작동하여 정확한 이메일 형식만 허용합니다.
세 번째 단계에서는 [[ -f config.txt && -r config.txt ]]로 파일이 존재하고(AND) 읽을 수 있는지 한 번에 검사합니다. 단일 대괄호에서는 &&를 밖에서 사용해야 하지만([ -f file ] && [ -r file ]), 이중 대괄호에서는 안에서 바로 사용할 수 있어 훨씬 간결합니다.
마지막으로, 두 조건이 모두 참일 때만 성공 메시지가 출력됩니다. 여러분이 이 코드를 사용하면 사용자 입력 검증, 파일 이름 필터링, 로그 파일 파싱 등 복잡한 문자열 처리를 안전하고 효율적으로 할 수 있습니다.
실무에서는 웹 애플리케이션의 입력값 검증, CI/CD 파이프라인에서 브랜치 이름 검사, 설정 파일 형식 확인 등에 널리 사용됩니다. 특히 정규표현식 지원 덕분에 복잡한 검증 로직을 외부 프로그램(grep, sed) 없이 구현할 수 있어 성능도 향상됩니다.
실전 팁
💡 [[ ]]는 Bash 전용이므로 sh 호환성이 필요하면 [ ]를 사용하세요. #!/bin/bash를 명시했다면 [[ ]]를 적극 활용하는 것이 좋습니다.
💡 정규표현식을 사용할 때는 패턴을 쿼테이션으로 감싸지 마세요. [[ $var =~ pattern ]]이 맞고, [[ $var =~ "pattern" ]]은 문자열 리터럴로 취급됩니다.
💡 패턴 매칭은 ==와 !=에서만 작동하고, 정규표현식은 =~에서만 작동합니다. 용도에 맞게 선택하세요.
💡 복잡한 정규표현식은 변수에 저장하면 가독성이 좋아집니다: regex='^[0-9]+$'; if [[ $input =~ $regex ]]; then
💡 정규표현식 매칭 결과는 BASH_REMATCH 배열에 저장됩니다. ${BASH_REMATCH[0]}은 전체 매칭, ${BASH_REMATCH[1]}부터는 캡처 그룹입니다.
4. 숫자 문자열 파일 조건 테스트
시작하며
여러분이 모니터링 스크립트를 작성할 때 이런 상황을 겪어본 적 있나요? "CPU 사용률이 80을 넘으면 경고를 보내야 하는데, 문자열 비교를 했더니 8이 80보다 크다고 나오네?
그리고 파일 권한은 어떻게 확인하지?" 이런 문제는 조건 테스트의 종류를 제대로 구분하지 않아서 발생합니다. Bash에서는 숫자 비교, 문자열 비교, 파일 테스트가 각각 다른 연산자를 사용하며, 잘못 사용하면 완전히 다른 결과가 나옵니다.
특히 "10"과 "9"를 문자열로 비교하면 사전순으로 "10"이 더 작다고 판단되는 함정에 빠지기 쉽습니다. 바로 이럴 때 필요한 것이 각 데이터 타입에 맞는 조건 테스트 연산자입니다.
숫자는 -eq/-lt/-gt, 문자열은 ==/!=, 파일은 -f/-d/-r 같은 전용 연산자를 사용하면 정확한 비교가 가능합니다.
개요
간단히 말해서, Bash에서는 비교하려는 데이터의 종류(숫자, 문자열, 파일)에 따라 전용 연산자를 사용해야 올바른 결과를 얻을 수 있습니다. 시스템 관리 스크립트에서는 다양한 종류의 데이터를 검사해야 합니다.
예를 들어, "메모리 사용량(숫자), 호스트 이름(문자열), 로그 파일 존재 여부(파일)" 같은 것들을 동시에 확인하는 경우가 많으므로 각 타입별 연산자를 정확히 알아야 합니다. 전통적으로는 모든 것을 문자열로 취급하여 비교하다가 숫자 비교에서 오류가 발생했습니다.
이제는 -eq(equal), -ne(not equal), -lt(less than), -gt(greater than) 같은 숫자 전용 연산자를 사용하면 산술 비교가 정확하게 이루어집니다. 조건 테스트 연산자의 핵심 특징은 세 가지 카테고리로 나뉩니다.
첫째, 숫자 비교는 -eq, -ne, -lt, -le, -gt, -ge를 사용하여 산술적으로 비교합니다. 둘째, 문자열 비교는 =, !=, <, >, -z(빈 문자열), -n(비어있지 않음)을 사용하여 사전순으로 비교합니다.
셋째, 파일 테스트는 -e(존재), -f(일반 파일), -d(디렉토리), -r(읽기 가능), -w(쓰기 가능), -x(실행 가능), -s(크기가 0보다 큼) 등을 사용합니다. 이러한 구분이 정확한 로직 구현에 필수적이기 때문에 중요합니다.
코드 예제
#!/bin/bash
# 시스템 상태 종합 체크 스크립트
# 숫자 비교 - CPU 사용률 체크
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1 | cut -d'.' -f1)
if [ $CPU_USAGE -gt 80 ]; then
echo "경고: CPU 사용률이 ${CPU_USAGE}%로 높습니다."
fi
# 문자열 비교 - 환경 변수 체크
ENVIRONMENT=${ENV:-development}
if [ "$ENVIRONMENT" = "production" ]; then
echo "프로덕션 모드로 실행 중입니다."
elif [ "$ENVIRONMENT" != "development" ]; then
echo "알 수 없는 환경: $ENVIRONMENT"
fi
# 빈 문자열 체크
if [ -z "$API_KEY" ]; then
echo "오류: API_KEY가 설정되지 않았습니다."
exit 1
fi
# 파일 테스트 - 로그 파일 검사
LOG_FILE="/var/log/myapp.log"
if [ -f "$LOG_FILE" ] && [ -s "$LOG_FILE" ]; then
# -s: 파일이 존재하고 크기가 0보다 큼
echo "로그 파일이 존재하고 데이터가 있습니다."
if [ -r "$LOG_FILE" ]; then
tail -n 20 "$LOG_FILE"
fi
fi
# 디렉토리와 권한 체크
DATA_DIR="/data"
if [ ! -d "$DATA_DIR" ]; then
echo "데이터 디렉토리가 없습니다. 생성합니다."
mkdir -p "$DATA_DIR"
fi
if [ -w "$DATA_DIR" ]; then
echo "데이터 디렉토리에 쓰기 권한이 있습니다."
else
echo "오류: 쓰기 권한이 없습니다."
exit 1
fi
설명
이것이 하는 일: 이 스크립트는 CPU 사용률(숫자), 환경 변수(문자열), 로그 파일(파일)을 각각 적절한 연산자로 검사하여 시스템 상태를 종합적으로 확인합니다. 첫 번째로, [ $CPU_USAGE -gt 80 ]는 CPU 사용률을 숫자로 비교합니다.
-gt는 "greater than"의 약자로, 왼쪽 숫자가 오른쪽보다 크면 참입니다. 만약 여기서 실수로 [ "$CPU_USAGE" > "80" ]처럼 문자열 비교를 사용하면, "9"가 "80"보다 사전순으로 뒤에 있어서 90%일 때도 경고가 안 나올 수 있습니다.
이렇게 숫자 전용 연산자를 사용하는 이유는 산술적 크기 비교가 필요하기 때문입니다. 그 다음으로, [ "$ENVIRONMENT" = "production" ]과 [ -z "$API_KEY" ]가 문자열을 검사합니다.
= 연산자는 두 문자열이 정확히 일치하는지 확인하고, -z는 "zero length", 즉 빈 문자열인지 검사합니다. 내부에서는 바이트 단위로 문자열을 비교하며, 대소문자도 구분합니다.
문자열 변수는 반드시 "$VAR"처럼 쿼테이션으로 감싸야 공백이 포함된 경우에도 안전합니다. 세 번째 단계에서는 [ -f "$LOG_FILE" ] && [ -s "$LOG_FILE" ]로 파일이 일반 파일이면서 크기가 0보다 큰지 확인합니다.
-f는 파일 타입을, -s는 파일 크기를 검사합니다. 그 다음 [ -r "$LOG_FILE" ]로 읽기 권한을 확인한 후 tail 명령으로 마지막 20줄을 출력합니다.
마지막으로, [ ! -d "$DATA_DIR" ]로 디렉토리가 없으면 생성하고, [ -w "$DATA_DIR" ]로 쓰기 권한을 확인하여 데이터를 안전하게 저장할 수 있는 환경인지 검증합니다.
여러분이 이 코드를 사용하면 시스템 모니터링, 배포 전 환경 검증, 백업 스크립트 작성 등에서 각 데이터를 정확하게 검사할 수 있습니다. 실무에서는 CI/CD 파이프라인에서 빌드 전 사전 조건 확인, 크론잡에서 주기적 상태 체크, 설치 스크립트에서 시스템 요구사항 검증 등에 활용됩니다.
특히 파일 권한과 존재 여부를 미리 확인하면 "permission denied"나 "file not found" 오류를 사전에 방지할 수 있습니다.
실전 팁
💡 숫자 비교에서 부등호(<, >)를 사용하려면 [ ]가 아닌 (( ))를 사용하세요: if (( CPU > 80 )). 더 직관적입니다.
💡 문자열이 비어있는지 확인할 때는 [ -z "$VAR" ]보다 [[ -z $VAR ]]가 더 안전합니다. 변수를 쿼테이션 없이 사용해도 됩니다.
💡 파일 비교 연산자 -nt(newer than), -ot(older than)를 사용하면 파일 수정 시간을 비교할 수 있어 백업 스크립트에 유용합니다.
💡 실행 권한을 확인할 때는 [ -x "$FILE" ]를 사용하세요. 스크립트나 바이너리를 실행하기 전에 권한을 확인하면 안전합니다.
💡 심볼릭 링크를 검사할 때는 -L 연산자를 사용하세요. [ -L "$FILE" ]는 심볼릭 링크이면 참이고, -f는 링크가 가리키는 실제 파일을 검사합니다.
5. case 문 패턴 매칭
시작하며
여러분이 CLI 도구를 만들 때 이런 상황을 겪어본 적 있나요? "사용자 명령어가 start, stop, restart, status 중 하나인지 확인하고 각각 다른 작업을 해야 하는데, if-elif를 10개나 쓰려니 코드가 너무 지저분해..." 이런 문제는 선택지가 많을 때 if-elif 체인이 길어지면서 가독성이 급격히 떨어지는 것에서 발생합니다.
특히 문자열 패턴을 여러 개 비교해야 할 때는 코드가 반복적이고 유지보수하기 어려워집니다. 게다가 와일드카드나 다중 패턴을 사용하려면 if 문으로는 구현이 매우 복잡해집니다.
바로 이럴 때 필요한 것이 case 문입니다. 이것은 하나의 변수를 여러 패턴과 비교하여 매칭되는 첫 번째 블록을 실행하는 구조로, 다른 언어의 switch 문과 비슷하지만 훨씬 강력한 패턴 매칭 기능을 제공합니다.
개요
간단히 말해서, case 문은 하나의 값을 여러 패턴과 순차적으로 비교하여 일치하는 패턴의 코드 블록을 실행하는 다중 분기 구조입니다. 실무에서는 명령줄 인터페이스(CLI) 도구를 만들거나 설정 파일을 파싱할 때 case 문이 필수적입니다.
예를 들어, "서비스 관리 스크립트에서 start/stop/restart 명령 처리하기", "파일 확장자에 따라 다른 처리 하기", "사용자 입력을 메뉴 시스템으로 라우팅하기" 같은 경우에 매우 적합합니다. 전통적으로는 긴 if-elif-else 체인을 사용했다면, 이제는 case 문으로 패턴을 명확하게 나열하여 가독성을 크게 높일 수 있습니다.
특히 와일드카드(*, ?)나 파이프(|)로 여러 패턴을 한 번에 매칭할 수 있어 훨씬 유연합니다. case 문의 핵심 특징은 네 가지입니다.
첫째, 와일드카드 패턴 매칭을 지원하여 *.txt 같은 표현을 사용할 수 있습니다. 둘째, 파이프(|)로 여러 패턴을 OR 조건으로 묶을 수 있습니다.
셋째, 각 블록은 ;;로 종료되며, 매칭되면 다음 패턴은 검사하지 않습니다(fall-through 없음). 넷째, *) 패턴으로 기본 케이스(default)를 지정할 수 있습니다.
이러한 특징들이 복잡한 분기 로직을 간결하고 명확하게 표현할 수 있게 해주기 때문에 중요합니다.
코드 예제
#!/bin/bash
# 서비스 관리 스크립트
SERVICE_NAME="myapp"
ACTION=$1
case $ACTION in
start)
# 서비스 시작
echo "서비스를 시작합니다..."
systemctl start $SERVICE_NAME
;;
stop)
# 서비스 중지
echo "서비스를 중지합니다..."
systemctl stop $SERVICE_NAME
;;
restart|reload)
# 재시작 또는 리로드 (둘 다 같은 동작)
echo "서비스를 재시작합니다..."
systemctl restart $SERVICE_NAME
;;
status)
# 상태 확인
systemctl status $SERVICE_NAME
;;
*.conf)
# 설정 파일 패턴 매칭
echo "설정 파일을 로드합니다: $ACTION"
source "$ACTION"
;;
[0-9]*)
# 숫자로 시작하는 경우
echo "포트 번호로 해석: $ACTION"
;;
*)
# 기본 케이스 (일치하는 패턴 없음)
echo "사용법: $0 {start|stop|restart|status}"
exit 1
;;
esac
설명
이것이 하는 일: 이 스크립트는 사용자가 입력한 명령어(start, stop 등)를 case 문으로 매칭하여 해당하는 서비스 관리 작업을 수행합니다. 첫 번째로, case $ACTION in으로 case 문을 시작하여 $ACTION 변수의 값을 아래 패턴들과 비교합니다.
패턴은 위에서 아래로 순서대로 검사되며, 첫 번째로 일치하는 패턴의 블록만 실행됩니다. 각 패턴 뒤에는 )가 붙어서 블록의 시작을 표시합니다.
그 다음으로, restart|reload) 같은 패턴은 파이프로 여러 값을 OR 조건으로 묶습니다. 이렇게 하면 "restart"나 "reload" 둘 중 하나만 입력해도 같은 블록이 실행됩니다.
각 블록은 ;;(세미콜론 두 개)로 종료되며, 이것을 만나면 case 문을 빠져나와서 esac 다음으로 이동합니다. 세 번째 단계에서는 .conf)와 [0-9])처럼 와일드카드와 문자 클래스를 사용한 패턴 매칭이 이루어집니다.
*.conf는 ".conf로 끝나는 모든 문자열"을 의미하고, [0-9]*는 "숫자로 시작하는 모든 문자열"을 의미합니다. 이런 유연한 패턴 덕분에 if 문으로는 복잡했을 조건을 간단히 표현할 수 있습니다.
마지막으로, *) 패턴은 와일드카드 *가 "모든 것"과 매칭되므로 기본 케이스(default case)로 작동합니다. 위의 모든 패턴과 일치하지 않을 때 실행되며, 보통 사용법을 출력하거나 오류를 표시합니다.
esac(case를 거꾸로 쓴 것)로 case 문이 종료됩니다. 여러분이 이 코드를 사용하면 CLI 도구, 시스템 관리 스크립트, 인터랙티브 메뉴, 파일 타입별 처리기 등을 깔끔하게 구현할 수 있습니다.
실무에서는 도커/쿠버네티스 헬퍼 스크립트, 배포 환경 선택기(dev/staging/prod), 빌드 도구의 서브커맨드 라우팅 등에 널리 사용됩니다. 특히 systemd 서비스 스크립트나 init.d 스크립트에서는 case 문이 거의 표준처럼 사용됩니다.
실전 팁
💡 패턴은 순서가 중요합니다. 구체적인 패턴을 먼저, 일반적인 패턴을 나중에 배치하세요. *) 기본 케이스는 항상 마지막에 둡니다.
💡 ;;&를 사용하면 매칭 후에도 다음 패턴을 계속 검사합니다(Bash 4.0+). fall-through가 필요한 경우에 유용합니다.
💡 case 문 안에서도 복잡한 명령을 실행할 수 있지만, 코드가 길어지면 함수로 분리하는 것이 좋습니다: start) start_service ;;
💡 대소문자를 무시하려면 Bash 4.0+에서 shopt -s nocasematch를 사용하거나, 패턴에서 [Ss]tart처럼 명시하세요.
💡 파일 이름 패턴 매칭에는 extglob를 활성화하면 더 강력합니다: shopt -s extglob; case $file in +(.jpg|.png)) ...
6. 논리 연산자 (&&, , !)
시작하며
여러분이 배포 스크립트를 작성할 때 이런 요구사항을 받은 적 있나요? "테스트가 성공하고, 빌드도 성공하고, 운영 환경이면 배포를 진행하되, 하나라도 실패하면 즉시 중단해야 해." 이런 문제는 여러 조건을 조합해야 할 때 매우 흔하게 발생합니다.
단순히 하나의 조건만 검사하는 것이 아니라, "A이고 B이면", "A이거나 B이면", "A가 아니면" 같은 복합 조건이 필요한 경우가 실무에서는 대부분입니다. 이럴 때 조건을 각각 따로 if 문으로 쓰면 중첩이 깊어지고 읽기 어려워집니다.
바로 이럴 때 필요한 것이 논리 연산자입니다. &&(AND), ||(OR), !(NOT) 세 가지 연산자를 사용하면 여러 조건을 하나의 표현식으로 간결하게 조합할 수 있으며, 단축 평가(short-circuit evaluation) 덕분에 효율적이기도 합니다.
개요
간단히 말해서, 논리 연산자는 여러 조건을 AND(모두 참), OR(하나라도 참), NOT(반대)로 조합하여 복잡한 조건을 표현하는 도구입니다. 실무 스크립트에서는 거의 모든 곳에서 논리 연산자가 필요합니다.
예를 들어, "파일이 존재하고 읽기 가능하면 처리하기", "개발 환경이거나 테스트 환경이면 디버그 모드 활성화", "프로덕션 환경이 아니면 경고 없이 강제 삭제" 같은 경우에 논리 연산자로 조건을 명확하게 표현할 수 있습니다. 전통적으로는 중첩된 if 문을 사용했다면, 이제는 &&와 ||를 사용하여 한 줄로 조건을 연결할 수 있습니다.
특히 Bash에서는 명령어의 성공/실패를 기준으로 논리 연산이 이루어지므로, command1 && command2 같은 패턴이 매우 자연스럽습니다. 논리 연산자의 핵심 특징은 세 가지입니다.
첫째, &&(AND)는 왼쪽이 성공(참)일 때만 오른쪽을 실행합니다(단축 평가). 둘째, ||(OR)는 왼쪽이 실패(거짓)일 때만 오른쪽을 실행합니다.
셋째, !(NOT)는 조건의 결과를 반대로 뒤집습니다. 이러한 특징들이 복잡한 조건 로직을 효율적이고 안전하게 구현할 수 있게 해주기 때문에 중요합니다.
코드 예제
#!/bin/bash
# 배포 스크립트 - 논리 연산자 활용
PROJECT_DIR="/var/www/myapp"
ENVIRONMENT=${ENV:-staging}
# AND 연산자 - 모든 조건이 참이어야 실행
if [ -d "$PROJECT_DIR" ] && [ -w "$PROJECT_DIR" ] && [ "$ENVIRONMENT" = "production" ]; then
echo "프로덕션 배포를 시작합니다."
cd "$PROJECT_DIR" && git pull && npm install && npm run build
fi
# 명령어 체이닝 - 앞이 성공해야 다음 실행
cd "$PROJECT_DIR" && \
npm test && \
npm run build && \
echo "빌드 성공!" || echo "빌드 실패!"
# OR 연산자 - 하나라도 참이면 실행
if [ "$ENVIRONMENT" = "development" ] || [ "$ENVIRONMENT" = "test" ]; then
echo "디버그 모드를 활성화합니다."
export DEBUG=true
fi
# NOT 연산자 - 조건을 반대로
if [ ! -f "package.json" ]; then
echo "오류: Node.js 프로젝트가 아닙니다."
exit 1
fi
# 복합 조건 - [[ ]] 안에서 논리 연산자 사용
if [[ -f "docker-compose.yml" && ( "$ENVIRONMENT" = "dev" || "$ENVIRONMENT" = "test" ) ]]; then
echo "도커 컴포즈로 개발 환경을 시작합니다."
docker-compose up -d
fi
# 단축 평가 활용 - 변수 기본값 설정
[ -z "$PORT" ] && PORT=3000
echo "포트: $PORT"
설명
이것이 하는 일: 이 스크립트는 논리 연산자를 사용하여 배포 조건을 검사하고, 명령어를 안전하게 체이닝하며, 환경별 설정을 분기합니다. 첫 번째로, [ -d "$PROJECT_DIR" ] && [ -w "$PROJECT_DIR" ] && [ "$ENVIRONMENT" = "production" ]는 세 가지 조건을 모두 검사합니다.
&& 연산자는 왼쪽이 성공(exit code 0)일 때만 오른쪽을 실행하므로, 디렉토리가 존재하지 않으면 쓰기 권한 검사는 실행되지 않습니다. 이런 단축 평가(short-circuit evaluation) 덕분에 불필요한 검사를 생략하여 효율적입니다.
세 조건이 모두 참일 때만 프로덕션 배포가 진행됩니다. 그 다음으로, cd "$PROJECT_DIR" && git pull && npm install && npm run build는 명령어 체이닝의 대표적인 예입니다.
cd가 실패하면(디렉토리가 없으면) 나머지는 실행되지 않아 안전합니다. 내부에서는 각 명령어의 exit code를 검사하여, 0이면 다음 명령어를 실행하고, 0이 아니면 전체 체인이 중단됩니다.
마지막 줄의 || echo "빌드 실패!"는 앞의 전체 체인이 실패했을 때만 실행됩니다. 세 번째 단계에서는 [ "$ENVIRONMENT" = "development" ] || [ "$ENVIRONMENT" = "test" ]로 OR 조건을 사용합니다.
환경이 development이면 두 번째 조건은 검사하지 않고 바로 참이 됩니다. || 연산자는 왼쪽이 실패했을 때만 오른쪽을 실행하므로, 하나라도 참이면 전체가 참입니다.
그리고 [ ! -f "package.json" ]는 NOT 연산자로 파일이 존재하지 않으면 참이 됩니다.
마지막으로, [[ ]] 안에서 &&와 ||를 사용하고 괄호로 그룹핑하여 복잡한 조건을 명확하게 표현합니다. [[ 조건1 && ( 조건2 || 조건3 ) ]]처럼 수학 공식처럼 우선순위를 지정할 수 있습니다.
[ -z "$PORT" ] && PORT=3000은 단축 평가를 활용한 관용구로, 변수가 비어있으면 기본값을 설정하는 간결한 방법입니다. 여러분이 이 코드를 사용하면 배포 스크립트, CI/CD 파이프라인, 설치 스크립트, 데이터베이스 마이그레이션 등에서 복잡한 조건을 안전하고 간결하게 처리할 수 있습니다.
실무에서는 "테스트 통과 → 빌드 → 배포" 같은 단계별 실행, 환경별 조건 분기, 오류 처리 등에 논리 연산자가 필수적입니다. 특히 명령어 체이닝은 실패 시 자동 중단되므로 안전한 자동화의 핵심입니다.
실전 팁
💡 명령어 체이닝에서 긴 줄은 백슬래시()로 나누세요. 가독성이 좋아지고 각 단계가 명확해집니다.
💡 복잡한 조건은 [[ ]] 안에서 &&와 ||를 사용하고, 괄호로 우선순위를 명확히 하세요: [[ ( A || B ) && C ]]
💡 || 연산자는 기본값 설정에 유용합니다: CONFIG_FILE=${1:-/etc/myapp.conf}보다 [ -z "$1" ] || CONFIG_FILE=$1이 더 읽기 쉬울 수 있습니다.
💡 디버깅할 때는 set -e를 사용하면 모든 명령어 실패 시 스크립트가 자동 종료됩니다. && 체이닝 없이도 안전합니다.
💡 ! 연산자를 [ ] 밖에서 사용할 수도 있습니다: ! command는 명령어 실행 결과를 반대로 만듭니다.
댓글 (0)
함께 보면 좋은 카드 뉴스
Client VPN으로 프라이빗 리소스 접근하기
AWS Client VPN을 활용하여 프라이빗 서브넷의 RDS, EC2 등 내부 리소스에 안전하게 접근하는 방법을 단계별로 알아봅니다. 인증서 생성부터 실제 연결까지 실무에서 바로 적용할 수 있는 완벽 가이드입니다.
NAT 게이트웨이로 프라이빗 서브넷 인터넷 연결 완벽 가이드
AWS VPC의 프라이빗 서브넷에서 인터넷에 접근하는 방법을 NAT 게이트웨이를 통해 배웁니다. 보안을 유지하면서 외부 API 호출, 패키지 업데이트 등을 수행하는 핵심 네트워크 아키텍처를 쉽게 이해할 수 있습니다.
Kurtosis 기본 명령어 마스터
분산 시스템과 블록체인 노드를 격리된 환경에서 손쉽게 테스트할 수 있는 Kurtosis의 핵심 명령어를 다룹니다. 초급 개발자도 바로 실무에 적용할 수 있도록 각 명령어의 개념과 활용법을 친절하게 설명합니다.
리눅스 기초 명령어 완벽 가이드
터미널을 처음 접하는 개발자를 위한 리눅스 핵심 명령어 가이드입니다. 쉘 기본 명령어부터 SSH 접속까지 실무에서 바로 활용할 수 있는 내용을 담았습니다.
컨테이너와 Docker 기초 완벽 가이드
컨테이너 기술의 핵심 개념부터 Docker를 활용한 실무 배포까지 단계별로 배웁니다. 가상머신과의 차이점, 이미지와 컨테이너의 관계, Dockerfile 작성법, 그리고 포트 포워딩과 볼륨 마운트까지 초급 개발자를 위해 쉽게 설명합니다.