조건 복합 명령 — if · case · [[ ]] · (( ))

이 글은 GNU Bash 매뉴얼 3.2.5.2 Conditional Constructs6.4 Bash Conditional Expressions를, 처음 배우는 분도 따라올 수 있게 쉬운 말로 풀어 정리합니다. “이 조건이면 A, 아니면 B”처럼 ​갈라지는 길 을 만드는 방법이 핵심입니다.

이 글을 읽고 나면

  • if / elif / else로 일반 분기를 작성할 수 있습니다.

  • [[ ]]로 파일·문자열·패턴을 안전하게 검사할 수 있습니다.

  • (( ))로 숫자 조건을, case로 여러 패턴 갈래를 다룰 수 있습니다.

  • 명령의 ​종료 코드 가 곧 “참/거짓”이라는 점을 이해합니다.

어떤 조건을 쓸까

조건문도 종류가 많아 보이지만, 고르는 기준은 “​무엇을 검사할지”입니다.

검사할 것

알맞은 구문

한 줄 설명

파일이 있는가 / 읽을 수 있는가

[[ -f … ]]

파일·디렉터리 상태

문자열이 비었는가 / 같은가

[[ -z … ]] / ==

문자열

이름이 prod*처럼 생겼는가

[[ $x == prod* ]]

글롭 패턴

숫자가 큰가

(( n > 2 )) 또는 -gt

산술

값이 여러 갈래 중 어디인가

case

패턴별 분기

방금 명령이 성공했는가

if cmd; then

종료 코드

Text

파일·문자열·패턴 ──▶ [[ ... ]] 숫자 계산 ──────────▶ (( ... )) 여러 갈래 패턴 ─────▶ case ... esac 명령 성공/실패 ─────▶ if 명령; then

이전 글에서 && / ||로 짧게 이을 수 있다고 했습니다. 분기가 길어지거나 여러 갈래가 생기면 if / case가 훨씬 읽기 쉽습니다.

조건의 진짜 의미 — 종료 코드

셸에서 “참/거짓”은 수학의 true/false가 아니라 ​명령이 끝난 뒤의 숫자 입니다.

종료 코드

조건문에서의 뜻

0

참 (성공) → then으로

0이 아님

거짓 (실패) → else / 다음 검사로

그래서 아래 두 가지는 ​본질적으로 같은 동작 입니다.

Bash

if grep -q 'hello' notes.txt; then echo "found" fi # grep: 파일에서 'hello' 문자열이 있는지 찾는 명령입니다. # grep이 'hello'를 찾으면(성공, 종료 코드 0) then 블록이 실행되고, # 못 찾으면(주로 종료 코드 1) then을 건너뜁니다.

if / elif / else — 기본 분기

형태는 거의 항상 같습니다.

Bash

if 조건; then # 조건이 참일 때 elif 다른조건; then # 위는 거짓이고, 여기가 참일 때 else # 위에서 모두 거짓일 때 fi
  • then / fi는 예약어입니다. 빠뜨리면 문법이 깨집니다.

  • elif는 여러 번 쓸 수 있습니다.

  • else는 생략해도 됩니다.

Bash

if [[ -f "$path" ]]; then echo "file exists" elif [[ -d "$path" ]]; then echo "it is a directory" else echo "not found" fi

[[ ]] — Bash에서 조건을 쓰는 기본 도구

Bash 스크립트에서는 조건을 쓸 때 [[ ... ]]를 기본 으로 쓰는 편이 안전합니다. (POSIX의 [ / test보다 공백·글로브에서 덜 깨집니다.)

[[]] 사이는 “조건식”을 적는 자리입니다. 식이 참이면 종료 코드 0, 거짓이면 비0이 되어 if가 갈라집니다.

파일 검사 (자주 쓰는 것)

검사

-e 경로

존재한다 (종류 무관)

-f 경로

일반 파일이다

-d 경로

디렉터리이다

-L 경로

심볼릭 링크이다

-r / -w / -x

읽기 / 쓰기 / 실행 가능

-s 경로

존재하고 크기가 0보다 크다

Bash

if [[ -f "$demo_dir/notes.txt" && -r "$demo_dir/notes.txt" ]]; then echo "readable file" fi

[[ 안에서는 && / ||를 그대로 쓸 수 있습니다. (즉, &&는 왼쪽이 거짓일 때, ||는 왼쪽이 참일 때 각각 오른쪽 조건은 평가하지 않고 건너뜁니다. 이런 방식 덕분에 불필요한 비교나 에러 발생을 막을 수 있습니다.)

문자열 검사

검사

-z "$s"

비어 있다 (길이 0)

-n "$s"

비어 있지 않다

"$a" == "$b"

문자열이 같다

"$a" != "$b"

문자열이 다르다

Bash

if [[ -z "${empty:-}" ]]; then echo "empty is empty" fi if [[ "$name" == "Ada" ]]; then echo "name is Ada" fi

패턴 매칭 (== 오른쪽)

[[에서 ==(또는 =)의 ​오른쪽을 따옴표로 감싸지 않으면, 글롭 패턴으로 비교합니다. (prod*, *.txt 등)

Bash

mode="release" if [[ "$mode" == rel* ]]; then echo "mode starts with rel" fi

쓰고 싶은 것

쓰는 법

패턴으로 비교

[[ $x == prod* ]] (오른쪽 따옴표 X)

글자 그대로 비교

[[ $x == "$y" ]] (오른쪽 따옴표 O)

정규식까지 쓰고 싶다면 =~가 있습니다. ([[ $line =~ ^[0-9]+$ ]]) 처음에는 글롭 패턴만으로도 충분한 경우가 많습니다.

정수 비교 ([[ 안)

연산

-eq

같다

-ne

다르다

-lt / -le

작다 / 작거나 같다

-gt / -ge

크다 / 크거나 같다

Bash

if [[ "$count" -ge 3 ]]; then echo "count >= 3" fi

문자열 비교(==)와 숫자 비교(-eq)를 헷갈리지 마세요. "10" == "9"는 문자열이 다르고, [[ 10 -gt 9 ]]는 숫자로 참입니다.

(( )) — 숫자 계산이 조건일 때

산술식이 0이 아니면 참(종료 코드 0), 0이면 거짓 입니다.

Bash

count=3 if (( count > 2 )); then echo "count is greater than 2" fi

(( 안에서는 $ 없이도 변수를 쓸 수 있어, 숫자 반복·카운터와 잘 맞습니다. (이전 글의 for ((i=0; i<3; i++))와 같은 계열입니다.)

case — 여러 갈래 패턴

값이 “이 목록 중 어디에 해당하는가”를 나눌 때 if를 여러 번 쓰기보다 case가 읽기 쉽습니다.

Bash

case "$mode" in debug) echo "build=debug" ;; release) echo "build=release" ;; *) echo "build=unknown" ;; esac

동작을 쉽게 말하면:

  1. "$mode" 값을 위에서부터 패턴과 비교합니다.

  2. ​처음 맞는 갈래 만 실행합니다.

  3. 각 갈래는 ;;로 끝냅니다.

  4. *)는 “위에 아무것도 안 맞으면” — 기본값으로 자주 씁니다.

  5. 한 갈래에 여러 패턴은 |로 이을 수 있습니다. (debug|test))

Text

mode="release" │ ▼ debug? → 아니오 release? → 예 ──▶ echo build=release ──▶ 끝 *) → (도달 안 함)

[[[ — 짧게만

[ / test

[[ ]] (Bash)

이식성

POSIX에 가깝다

Bash 전용에 가깝다

공백·글로브

따옴표 실수가 잦다

상대적으로 덜 깨진다

&& 안에서

불편

자연스럽다

패턴 ==

없음(문자열만)

지원

Bash로 스크립트를 쓴다면 [[ ]]를 기본 으로 두고, 꼭 sh 호환이 필요할 때만 [를 쓰는 전략이 실무에서 흔합니다.

실행 예제

아래 스크립트는 if / [[ ]] / (( )) / case / “명령을 조건으로”를 한 화면에서 비교해 보여 줍니다.

Bash

#!/usr/bin/env bash # if / case / [[ ]] / (( )) 조건 분기를 한 화면에서 비교합니다. set -eu demo_dir="$(mktemp -d "${TMPDIR:-/tmp}/b05.XXXXXX")" cleanup() { rm -rf -- "$demo_dir"; } trap cleanup EXIT printf 'hello\n' >"$demo_dir/notes.txt" name="Ada" count=3 mode="release" echo "=== 1) if / elif / else ===" if [[ -f "$demo_dir/notes.txt" ]]; then # (1) 일반 파일이면 참 echo "file exists" elif [[ -d "$demo_dir/notes.txt" ]]; then # (2) 아니면 디렉터리인지 검사 echo "it is a directory" else echo "not found" fi echo echo "=== 2) [[ ]] 문자열 검사 ===" if [[ -z "${empty:-}" ]]; then # (3) 비어 있거나 없으면 참 echo "empty is empty" fi if [[ -n "$name" ]]; then # (4) 길이가 0보다 크면 참 echo "name=$name" fi if [[ "$name" == "Ada" ]]; then # (5) 문자열이 같은지 echo "name is Ada" fi echo echo "=== 3) [[ ]] 패턴 매칭 ===" if [[ "$mode" == rel* ]]; then # (6) 오른쪽은 글롭 패턴 (quote 안 함) echo "mode starts with rel" fi echo echo "=== 4) [[ ]] 파일·숫자·조합 ===" if [[ -f "$demo_dir/notes.txt" && -r "$demo_dir/notes.txt" ]]; then # (7) 둘 다 참일 때 echo "readable file" fi if [[ "$count" -ge 3 ]]; then # (8) 정수 비교 echo "count >= 3" fi echo echo "=== 5) (( )) 산술 조건 ===" if (( count > 2 )); then # (9) 산술식이 0이 아니면 참 echo "count is greater than 2" fi echo echo "=== 6) case: 여러 갈래 패턴 ===" case "$mode" in # (10) 위에서부터 첫 일치만 실행 debug) echo "build=debug" ;; release) echo "build=release" ;; *) echo "build=unknown" ;; esac echo echo "=== 7) 명령 자체를 조건으로 ===" if grep -q 'hello' "$demo_dir/notes.txt"; then # (11) 종료 코드 0이면 then echo "grep found hello" fi echo echo "ok — 조건은 '무엇을 검사할지'에 따라 골라 씁니다."

​주요 라인별 설명
1–2: if / elif — 파일인지 먼저 보고, 아니면 디렉터리인지 봅니다.
3–5: -z(비었음), -n(비지 않음), ==(문자열 같음)을 검사합니다.
6: == 오른쪽 rel*는 패턴입니다. 따옴표를 치면 글자 그대로가 됩니다.
7–8: 파일 권한과 정수 비교(-ge)를 조합합니다.
9: (( ))는 숫자 계산 결과가 0이 아니면 참입니다.
10: case는 위에서부터 첫 패턴만 실행하고, *)는 기본값입니다.
11: grep -q의 성공/실패(종료 코드)가 곧 if의 조건이 됩니다.

실행하면 대략 이런 결과가 나옵니다.

Text

=== 1) if / elif / else === file exists === 2) [[ ]] 문자열 검사 === empty is empty name=Ada name is Ada === 3) [[ ]] 패턴 매칭 === mode starts with rel === 4) [[ ]] 파일·숫자·조합 === readable file count >= 3 === 5) (( )) 산술 조건 === count is greater than 2 === 6) case: 여러 갈래 패턴 === build=release === 7) 명령 자체를 조건으로 === grep found hello

헷갈리기 쉬운 부분

  1. [ $a = $b ]처럼 따옴표 없이 — 값이 비어 있으면 문법이 깨집니다. Bash라면 [[ "$a" == "$b" ]]를 쓰세요.

  2. ​패턴을 리터럴로 비교하고 싶은데 따옴표를 안 침[[ $x == "$y" ]]처럼 오른쪽을 따옴표로 감싸야 글자 그대로입니다.

  3. ==-eq 혼동 — 문자열은 ==, 정수는 -eq/-gt 또는 (( )).

  4. case에서 ;; 빠뜨림 — 각 갈래 끝에 ;;가 필요합니다.

  5. ​명령 조건을 [[ grep ... ]]처럼 감싸기 — 명령을 조건으로 쓸 때는 if grep ...; then처럼 ​명령 자체를 if 뒤에 둡니다.

한 줄 요약

​조건의 본질은 종료 코드(0=참)이고, 파일·문자열·패턴은 [[ ]], 숫자는 (( )), 여러 갈래는 case, 긴 분기는 if로 씁니다.

On this page