iBetter Books
수정

지역변수와 스코프

함수를 쓰기 시작하면 예상치 못한 버그를 만납니다. 함수 안에서 i라는 변수를 썼는데, 함수를 호출하고 나서 밖의 i가 바뀌어 있는 겁니다. Bash에서 함수 안의 변수는 기본적으로 전역(global)입니다. local을 붙이지 않으면 스크립트 전체에서 공유됩니다.

전역 오염 문제 확인

직접 확인해보겠습니다.

#!/bin/bashcount=10   # 전역 변수increment() {    count=99   # local 없이 수정 → 전역 변수 변경    echo "함수 안: count = $count"}echo "호출 전: count = $count"incrementecho "호출 후: count = $count"   # 99로 바뀜!

실행 결과는 다음과 같습니다.

호출 전: count = 10
함수 안: count = 99
호출 후: count = 99

increment 함수가 전역 변수 count를 바꿔버렸습니다. 함수가 많아지고 스크립트가 커지면 이런 문제가 원인을 찾기 어렵게 됩니다.

반복문에서 자주 쓰는 i도 위험합니다.

#!/bin/bashi=0loop_func() {    for i in 1 2 3; do   # 전역 i 를 변경        echo "함수 안 i: $i"    done}loop_funcecho "함수 밖 i: $i"   # 3 — 함수가 바꿔버림

local 키워드

local을 쓰면 그 변수는 함수 안에서만 존재합니다. 함수가 끝나면 사라지고, 같은 이름의 전역 변수에 영향을 주지 않습니다.

#!/bin/bashcount=10increment() {    local count=99   # 이 count는 함수 안에서만 존재    echo "함수 안: count = $count"}echo "호출 전: count = $count"incrementecho "호출 후: count = $count"   # 10 그대로

실행 결과는 다음과 같습니다.

호출 전: count = 10
함수 안: count = 99
호출 후: count = 10

전역 count가 그대로입니다. 함수 안의 count는 함수 안에서만 살다가 사라졌습니다.

local은 선언과 동시에 값을 할당할 수 있습니다. 또는 먼저 선언하고 나중에 할당할 수도 있습니다.

func() {    local name="홍길동"     # 선언과 동시에 할당    local age               # 먼저 선언    age=30                  # 나중에 할당    local result    result=$(some_command)  # 명령 치환 결과 저장}

명령 치환 결과를 local로 받을 때 주의사항이 하나 있습니다. local result=$(some_command) 처럼 한 줄로 쓰면 종료 코드가 항상 0이 됩니다. local 명령 자체가 성공하면 0을 반환하기 때문입니다. 명령이 실패했는지 확인해야 한다면 두 줄로 분리하는 것이 안전합니다.

func() {    local result    result=$(some_command)    # 이 경우 $?가 some_command의 종료 코드    if [[ $? -ne 0 ]]; then        echo "오류 발생" >&2        return 1    fi}

local 사용 권장 규칙

함수 안에서 사용하는 변수는 모두 local로 선언하는 것을 권장합니다. 규모가 작은 스크립트에서는 큰 문제가 없어 보여도, 나중에 함수가 늘어나면 어디서 변수를 바꾸는지 추적하기 어려워집니다. 처음부터 습관을 들이는 것이 좋습니다.

함수에서 일부러 전역 변수를 변경해야 하는 경우라면, 명시적인 주석을 달아두는 것이 좋습니다.

# 전역 변수: g_result (함수가 결과를 여기에 저장)g_result=""compute() {    # 의도적으로 전역 g_result를 변경    g_result=$(( $1 * $2 ))}compute 6 7echo "결과: $g_result"   # 42

전역 변수임을 드러내기 위해 이름 앞에 g_를 붙이는 관례도 있습니다.

실습: 전역 vs 지역변수 차이 확인 스크립트

#!/bin/bash# 새 파일: scope_test.sh# 전역 변수username="admin"counter=0# local 없이 — 전역 오염bad_func() {    username="hacker"     # 전역 변수 덮어씀    counter=100           # 전역 변수 덮어씀    echo "[bad_func] username=$username, counter=$counter"}# local 사용 — 안전good_func() {    local username="guest"   # 지역 변수    local counter=1          # 지역 변수    echo "[good_func] username=$username, counter=$counter"}echo "=== 초기 상태 ==="echo "username=$username, counter=$counter"echo ""echo "=== bad_func 호출 ==="bad_funcecho "호출 후: username=$username, counter=$counter"echo ""echo "=== 변수 복원 ==="username="admin"counter=0echo "username=$username, counter=$counter"echo ""echo "=== good_func 호출 ==="good_funcecho "호출 후: username=$username, counter=$counter"

실행 결과는 다음과 같습니다.

=== 초기 상태 ===
username=admin, counter=0

=== bad_func 호출 ===
[bad_func] username=hacker, counter=100
호출 후: username=hacker, counter=100

=== 변수 복원 ===
username=admin, counter=0

=== good_func 호출 ===
[good_func] username=guest, counter=1
호출 후: username=admin, counter=0

bad_func는 전역 변수를 바꿔버리지만, good_func는 함수가 끝난 뒤에도 전역 변수가 그대로입니다. 결과만 놓고 보면 차이가 명확합니다. 함수 안에서 변수를 만들 때는 local을 붙이는 것을 기본 습관으로 삼습니다.