변수 스코프와 반환 상태

ShellBeginner
지금 연습하기

소개

스크립트를 작성할 때는 데이터에 접근하는 방식과 함수가 성공 또는 실패를 전달하는 방식을 관리해야 합니다.

서로 다른 두 위치에서 같은 변수 이름을 사용하면 한 변수가 다른 변수의 값을 실수로 덮어쓸 수 있습니다. 프로그래밍에서 흔히 발생하는 버그입니다. **스코프 (Scope)**를 사용하면 이 문제를 해결할 수 있습니다. 즉, 변수를 함수 내부에서만 사용하도록 제한합니다.

또한 함수는 작업이 성공했는지 실패했는지를 메인 스크립트에 알려야 합니다. Bash 에서는 반환 상태 코드를 사용해 이를 처리합니다.

이 실습에서는 규모가 큰 스크립트에서 특히 중요한 두 가지 개념을 함께 다룹니다. 변수의 값이 영향을 주면 안 되는 곳으로 유출되지 않도록 제한하는 방법과, 성공 또는 실패 상태를 사용해 실행 흐름을 제어하는 방법입니다.

이 실습에서는 다음을 배웁니다.

  1. 변수 스코프: local을 사용해 변수 충돌을 방지합니다.
  2. 반환 상태: 종료 코드 (성공은 0, 실패는 0 이 아닌 값) 를 사용해 스크립트 흐름을 제어합니다.

변수 스코프: 전역 변수와 지역 변수

Bash 에서 변수는 기본적으로 전역 (Global) 변수입니다. 따라서 함수 내부에서 정의한 변수도 스크립트의 어디에서나 접근하거나 변경할 수 있습니다. 이로 인해 스크립트의 한 부분이 다른 부분에서 필요한 데이터를 실수로 변경하는 등 복잡한 버그가 발생할 수 있습니다.

이를 방지하려면 local 키워드를 사용합니다. local 변수는 변수를 만든 함수 내부에서만 존재합니다.

이것은 셸 스크립팅의 장단점 중 하나입니다. Bash 는 빠르게 시작하기 쉽지만, 그 편리함 때문에 데이터가 예상하지 못한 방식으로 스크립트 전체에 유출되기도 쉽습니다. 스코프는 변수가 어디에서 보이는지를 제한하는 규칙입니다.

이 차이를 직접 확인합니다.

scope.sh라는 스크립트를 만듭니다.

cd ~/project
nano scope.sh

다음 코드를 추가합니다.

#!/bin/bash

## This is a Global variable
city="New York"

print_city() {
  ## This is a Local variable. It shares the name 'city', but it is a separate variable.
  local city="London"
  echo "Inside function: $city"
}

echo "Before function call: $city"

## Run the function
print_city

## Check if the Global variable was changed
echo "After function call: $city"

저장 (Ctrl+O, Enter) 한 다음 종료 (Ctrl+X) 합니다.

스크립트를 실행 가능하게 만든 후 실행합니다.

chmod +x scope.sh
./scope.sh

실행 결과:

Before function call: New York
Inside function: London
After function call: New York

함수 밖의 city 변수 값이 "New York"으로 유지된 것을 확인할 수 있습니다. 함수는 자체 local 복사본인 city("London") 를 사용했으며 전역 변수에는 영향을 주지 않았습니다.

local을 사용하지 않았다면 함수가 전역 변수의 값을 덮어썼을 것이며, 마지막 줄에는 "London"이 출력되었을 것입니다.

스크립트가 커질수록 local이 중요해지는 이유가 여기에 있습니다. 짧은 예제에서는 전역 변수가 문제가 없어 보일 수 있습니다. 하지만 유지 관리해야 하는 긴 스크립트에서는 같은 변수 이름을 재사용하면 원인을 찾기 어려운 미묘한 버그가 발생할 수 있습니다.

반환 상태 코드

많은 프로그래밍 언어에서 함수는 문자열이나 숫자 같은 데이터를 반환합니다. Bash 에서 return은 성공 또는 실패를 나타내는 상태 코드를 반환하는 데만 사용합니다.

  • 0: 성공
  • 1-255: 실패 (오류)

return과 echo를 혼동하지 마세요.

  • echo는 화면에 텍스트를 출력합니다. 출력 결과를 캡처하면 변수에 저장할 수도 있습니다.
  • return은 함수가 "성공했다" 또는 "실패했다"고 시스템에 알리는 숨겨진 숫자를 전달합니다.

이 구분은 셸 프로그래밍의 기본 개념입니다. 함수는 echo로 사람이 읽을 수 있는 정보를 출력할 수 있지만, return 값은 일반 데이터를 전달하는 용도가 아니라 실행 흐름을 제어하는 데 사용합니다.

반환 코드 5 를 사용해 실패를 시뮬레이션하는 함수를 만들어 보겠습니다.

status.sh를 만듭니다.

nano ~/project/status.sh

다음 코드를 추가합니다.

#!/bin/bash

check_file() {
  echo "Checking for file..."
  ## We simulate a specific error by returning Code 5
  return 5
}

check_file

저장한 다음 종료합니다.

스크립트를 실행 가능하게 만든 후 실행합니다.

chmod +x status.sh
./status.sh

출력:

Checking for file...

텍스트는 보이지만 숫자 5 는 어디에 있을까요? 숫자 5 는 화면에 표시되지 않은 채 반환되었습니다. 다음 단계에서는 이 값을 캡처하고 사용하는 방법을 배웁니다.

반환 코드 캡처하기 ($?)

마지막으로 실행한 명령이나 함수의 반환 코드를 확인하기 위해 Bash 는 특수 변수 $?를 제공합니다.

check_file 실행이 완전히 끝나면 $?에는 자동으로 값 5가 저장됩니다.

중요한 규칙: 명령이 실행된 직후 즉시 $?를 읽어야 합니다. 다른 명령을 실행하는 순간 (간단한 echo도 포함) $?는 새로 실행한 명령의 상태 코드로 업데이트됩니다.

반환 코드를 캡처하도록 status.sh를 수정합니다.

파일을 엽니다.

nano status.sh

스크립트를 다음과 같이 수정합니다.

#!/bin/bash

check_file() {
  echo "Checking for file..."
  return 5
}

check_file

## Capture the return code immediately into a variable
ret_val=$?

echo "The function returned: $ret_val"

저장한 다음 종료합니다. 다시 실행합니다.

./status.sh

출력:

Checking for file...
The function returned: 5

이제 오류 코드를 ret_val 변수에 캡처했습니다.

반환 코드를 캡처하면 스크립트의 나머지 부분에서 상황에 맞게 대응할 수 있습니다. 이후의 if 문에서 이 값을 확인해 계속 진행할지, 다시 시도할지, 중지할지를 결정할 수 있습니다. 이렇게 하면 함수가 서로 고립된 코드 조각으로 동작하지 않고, 규모가 큰 스크립트 안에서 서로 협력하게 됩니다.

반환 상태에 따라 처리하기

이제 상태 코드를 확인했으므로 if 문을 사용해 오류를 적절하게 처리할 수 있습니다.

  • 코드가 0이면 모든 작업이 정상입니다.
  • 코드가 5이면 무엇이 잘못되었는지 정확히 알 수 있습니다 (예: "파일이 없음").

표준 Linux 명령도 이와 같은 방식으로 동작합니다. 예를 들어 grep은 일치하는 항목을 찾으면 0을 반환하고, 찾지 못하면 1을 반환합니다.

로직을 추가하도록 status.sh를 수정합니다.

nano status.sh

조건문 로직을 추가합니다.

#!/bin/bash

check_file() {
  echo "Checking for file..."
  return 5
}

check_file
ret_val=$?

## Logic based on the return code
if [ $ret_val -eq 0 ]; then
  echo "Success: File found."
elif [ $ret_val -eq 5 ]; then
  echo "Error: File missing (Code 5)."
else
  echo "Unknown Error: Code $ret_val"
fi

저장한 다음 종료합니다. 스크립트를 실행합니다.

./status.sh

출력:

Checking for file...
Error: File missing (Code 5).

이제 기본적인 오류 처리를 구현했습니다. 파일 확인에 실패하면 스크립트가 이를 인식하고, 아무 설명 없이 계속 실행하거나 중단되는 대신 구체적인 오류 메시지를 출력할 수 있습니다.

요약

이 실습에서는 스크립팅의 핵심 개념 두 가지를 연습했습니다.

  1. 변수 스코프 (local): local을 사용하면 함수 내부의 변수가 전역 변수의 값을 덮어쓰지 않으므로 스크립트를 예측 가능한 방식으로 유지할 수 있습니다.
  2. 반환 상태 (return, $?): 종료 코드를 사용하면 함수가 성공 또는 실패를 메인 스크립트에 전달할 수 있습니다. 이를 통해 스크립트가 오류에 지능적으로 대응하도록 만들 수 있습니다.

전역 변수를 반드시 수정해야 하는 경우가 아니라면 함수 내부의 변수에는 항상 local을 사용하세요.