본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 11. 17. · 21 Views
Bash 변수와 환경변수 완벽 가이드
셸 스크립트의 핵심인 변수와 환경변수를 처음부터 끝까지 마스터해보세요. 변수 선언, 참조 방법, 지역/전역 변수의 차이, export 사용법, 특수 변수 활용까지 실무에서 바로 쓸 수 있는 모든 내용을 담았습니다.
목차
1. 변수 선언과 할당
시작하며
여러분이 셸 스크립트를 작성하다가 "변수명=값" 형태로 쓰면 되는데 왜 자꾸 에러가 날까 고민해본 적 있나요? 많은 초보자들이 "name = John"처럼 띄어쓰기를 넣어서 스크립트가 동작하지 않는 상황을 겪습니다.
이런 문제는 Bash의 변수 할당 문법이 다른 프로그래밍 언어와 조금 다르기 때문에 발생합니다. 띄어쓰기 하나만으로도 변수 할당이 아닌 명령어 실행으로 인식되어 오류가 발생하죠.
바로 이럴 때 필요한 것이 Bash 변수 선언의 정확한 규칙입니다. 등호 앞뒤에 공백을 넣지 않는 것만으로도 대부분의 에러를 피할 수 있습니다.
개요
간단히 말해서, Bash에서 변수는 "변수명=값" 형태로 선언하며, 등호 앞뒤에 공백이 없어야 합니다. 변수 선언이 필요한 이유는 데이터를 저장하고 재사용하기 위해서입니다.
예를 들어, 반복적으로 사용되는 파일 경로나 설정값을 변수에 저장하면 코드 수정이 훨씬 쉬워집니다. 전통적으로는 같은 값을 여러 번 타이핑했다면, 이제는 변수에 한 번만 저장하고 필요할 때마다 참조할 수 있습니다.
Bash 변수의 핵심 특징은 첫째, 타입 선언이 필요 없다는 점입니다. 둘째, 등호 앞뒤 공백을 허용하지 않습니다.
셋째, 변수명은 대소문자를 구분합니다. 이러한 특징들을 이해하면 변수 선언 시 실수를 크게 줄일 수 있습니다.
코드 예제
#!/bin/bash
# 올바른 변수 선언 방법
username="developer"
age=25
project_path="/home/user/projects"
# 여러 단어를 포함한 문자열 (따옴표 사용)
greeting="Hello, World!"
# 명령어 실행 결과를 변수에 저장
current_date=$(date +%Y-%m-%d)
file_count=$(ls -1 | wc -l)
# 숫자 연산 결과 저장
total=$((10 + 20))
echo "사용자: $username, 나이: $age"
echo "오늘 날짜: $current_date"
echo "파일 개수: $file_count"
설명
이것이 하는 일: Bash 변수는 데이터를 메모리에 저장하고 나중에 참조할 수 있게 해주는 컨테이너입니다. 첫 번째로, "username="developer""처럼 간단한 문자열을 저장할 수 있습니다.
등호 앞뒤에 공백이 없어야 하는 이유는 Bash가 공백을 명령어 구분자로 사용하기 때문입니다. 만약 "username = developer"라고 쓰면 Bash는 "username"이라는 명령어를 "="와 "developer"라는 인자와 함께 실행하려고 시도합니다.
두 번째로, 명령어 실행 결과를 변수에 저장할 때는 "$(...)" 문법을 사용합니다. 예를 들어 "current_date=$(date +%Y-%m-%d)"는 date 명령어의 결과를 current_date 변수에 저장합니다.
이는 실시간 데이터를 스크립트에서 활용할 수 있게 해줍니다. 세 번째로, 숫자 연산을 할 때는 "$(( ))" 문법을 사용합니다.
Bash는 기본적으로 모든 값을 문자열로 취급하기 때문에 산술 연산을 위해서는 특별한 문법이 필요합니다. 여러분이 이 방법을 사용하면 스크립트에서 동적인 값을 다루고, 코드 재사용성을 높이며, 유지보수가 쉬운 스크립트를 작성할 수 있습니다.
특히 설정값을 변수로 관리하면 한 곳만 수정해도 전체 스크립트에 변경사항이 적용됩니다.
실전 팁
💡 변수명은 의미 있게 지으세요. "a", "b" 대신 "user_count", "config_path"처럼 명확한 이름을 사용하면 나중에 코드를 읽기 쉽습니다.
💡 공백이 포함된 문자열은 반드시 따옴표로 감싸세요. "message=Hello World"는 에러가 나지만 "message="Hello World""는 정상 작동합니다.
💡 변수명은 영문자나 밑줄로 시작해야 하며 숫자로 시작할 수 없습니다. "1name"은 안 되지만 "name1"은 가능합니다.
💡 대문자 변수명은 보통 환경변수나 상수를 위해 예약하고, 일반 변수는 소문자나 스네이크 케이스를 사용하는 것이 관례입니다.
💡 명령어 결과를 저장할 때는 백틱(`...`) 대신 "$(...)" 문법을 사용하세요. 더 읽기 쉽고 중첩이 가능합니다.
2. 변수 참조 방법
시작하며
여러분이 변수를 사용하려고 "$filename.txt"라고 썼는데 "filename.txt"라는 변수를 찾을 수 없다는 에러를 본 적 있나요? 변수명 뒤에 바로 다른 문자가 오면 Bash가 어디까지가 변수명인지 구분하지 못하는 상황이 발생합니다.
이런 문제는 실제 파일 처리 스크립트에서 자주 발생합니다. 특히 동적으로 파일명을 생성하거나 문자열을 조합할 때 변수의 경계가 모호해지면서 의도하지 않은 결과가 나타납니다.
바로 이럴 때 필요한 것이 중괄호를 이용한 변수 참조 "${var}" 문법입니다. 변수의 경계를 명확히 하여 이런 문제를 완전히 해결할 수 있습니다.
개요
간단히 말해서, Bash에서 변수를 참조하는 방법은 "$var"와 "${var}" 두 가지가 있으며, 후자가 더 안전하고 명확합니다. 변수 참조가 필요한 이유는 저장된 값을 실제로 사용하기 위해서입니다.
예를 들어, 파일명을 조합하거나 문자열 중간에 변수 값을 삽입할 때 반드시 올바른 참조 방법을 사용해야 합니다. 전통적으로는 "$var" 형태만 사용했다면, 이제는 "${var}" 형태를 기본으로 사용하는 것이 권장됩니다.
특히 변수 뒤에 다른 문자가 바로 올 때는 필수입니다. 변수 참조의 핵심 특징은 첫째, 중괄호를 사용하면 변수명의 경계를 명확히 할 수 있습니다.
둘째, 변수 확장 기능을 사용할 때는 반드시 중괄호가 필요합니다. 셋째, 큰따옴표 안에서 변수를 참조하면 공백이 보존됩니다.
이러한 특징들을 이해하면 문자열 처리가 훨씬 정확해집니다.
코드 예제
#!/bin/bash
filename="report"
extension="txt"
# 기본 변수 참조
echo $filename
# 중괄호를 사용한 변수 참조 (권장)
echo ${filename}
# 변수 뒤에 문자가 올 때는 중괄호 필수
echo "${filename}.${extension}" # report.txt
echo "$filename_backup" # filename_backup 변수를 찾음 (의도와 다름)
echo "${filename}_backup" # report_backup (올바름)
# 공백이 포함된 변수 처리
path="/home/user/my documents"
ls $path # 에러 발생 (공백 때문에 두 개 인자로 인식)
ls "$path" # 정상 작동 (하나의 경로로 인식)
# 기본값 설정
echo "${undefined_var:-default_value}" # default_value 출력
설명
이것이 하는 일: 변수 참조는 변수에 저장된 값을 실제로 꺼내서 사용하는 과정입니다. 첫 번째로, 가장 기본적인 "$filename" 형태는 대부분의 경우 잘 작동합니다.
하지만 "${filename}.txt"처럼 변수명 뒤에 바로 다른 문자가 올 때는 중괄호가 없으면 Bash가 "filename.txt"라는 변수를 찾으려고 합니다. 중괄호는 "여기까지가 변수명입니다"라고 명확히 알려주는 역할을 합니다.
두 번째로, 큰따옴표 안에서 변수를 참조하는 것과 따옴표 없이 참조하는 것은 큰 차이가 있습니다. "$path"처럼 큰따옴표로 감싸면 공백이 보존되어 하나의 값으로 취급되지만, 따옴표 없이 $path라고 쓰면 공백을 기준으로 여러 개의 값으로 분리됩니다.
이는 파일 경로에 공백이 포함된 경우 치명적인 버그를 일으킵니다. 세 번째로, "${var:-default}" 같은 변수 확장 기능을 사용하면 변수가 정의되지 않았을 때 기본값을 제공할 수 있습니다.
이는 스크립트의 안정성을 크게 높여줍니다. 네 번째로, "${#var}" 문법으로 변수 값의 길이를 구하거나, "${var:0:5}" 문법으로 부분 문자열을 추출할 수 있습니다.
이런 고급 기능들은 모두 중괄호 안에서만 사용할 수 있습니다. 여러분이 이 방법을 사용하면 변수 참조 시 발생하는 버그를 예방하고, 더 강력한 문자열 처리 기능을 활용할 수 있으며, 코드의 가독성도 향상됩니다.
특히 공백이 포함된 경로를 다룰 때는 큰따옴표 사용이 필수입니다.
실전 팁
💡 항상 변수 참조 시 중괄호를 사용하는 습관을 들이세요. "${var}" 형태가 "$var"보다 안전하고 명확합니다.
💡 변수를 명령어 인자로 사용할 때는 반드시 큰따옴표로 감싸세요. 공백이 포함된 경로 문제를 예방할 수 있습니다.
💡 정의되지 않은 변수를 참조하면 빈 문자열이 반환됩니다. "set -u" 옵션을 스크립트 시작 부분에 추가하면 정의되지 않은 변수 사용 시 에러를 발생시켜 버그를 조기에 발견할 수 있습니다.
💡 변수 확장 기능 "${var:-default}"는 변수가 없거나 빈 문자열일 때 기본값을 제공합니다. "${var-default}"는 변수가 없을 때만 기본값을 제공합니다.
💡 배열 변수를 참조할 때는 "${array[@]}" 형태를 사용하세요. 큰따옴표 없이 쓰면 각 원소가 분리되어 의도하지 않은 결과를 초래할 수 있습니다.
3. 지역 변수와 전역 변수
시작하며
여러분이 함수 안에서 변수를 선언했는데 함수 밖에서도 그 값이 유지되어서 다른 함수의 동작에 영향을 준 경험이 있나요? 대규모 스크립트에서 이런 변수 충돌은 찾기 어려운 버그의 원인이 됩니다.
이런 문제는 Bash의 기본 변수가 전역 범위를 가지기 때문에 발생합니다. 함수 안에서 선언한 변수라도 특별히 지정하지 않으면 스크립트 전체에서 접근할 수 있어서 의도하지 않은 값 변경이 일어납니다.
바로 이럴 때 필요한 것이 local 키워드를 사용한 지역 변수 선언입니다. 함수 내부에서만 유효한 변수를 만들어 변수 충돌을 방지하고 코드의 예측 가능성을 높일 수 있습니다.
개요
간단히 말해서, Bash의 변수는 기본적으로 전역 범위를 가지며, local 키워드를 사용하면 함수 내부로 범위를 제한할 수 있습니다. 변수 범위를 구분하는 이유는 함수 간 간섭을 막고 코드의 안정성을 높이기 위해서입니다.
예를 들어, 같은 이름의 변수를 여러 함수에서 사용해도 local로 선언하면 서로 영향을 주지 않습니다. 전통적으로는 모든 변수가 전역이어서 고유한 이름을 일일이 지어야 했다면, 이제는 local을 사용하여 함수마다 독립적인 변수 공간을 가질 수 있습니다.
변수 범위의 핵심 특징은 첫째, 기본적으로 모든 변수는 전역입니다. 둘째, local 키워드는 함수 내부에서만 사용 가능합니다.
셋째, 지역 변수는 함수 종료 시 자동으로 소멸됩니다. 이러한 특징들을 이해하면 더 모듈화된 안전한 스크립트를 작성할 수 있습니다.
코드 예제
#!/bin/bash
# 전역 변수
global_counter=0
function increment_global() {
# local 없이 선언하면 전역 변수를 수정
global_counter=$((global_counter + 1))
echo "함수 내부 global_counter: $global_counter"
}
function use_local() {
# local 키워드로 지역 변수 선언
local local_counter=0
local_counter=$((local_counter + 1))
echo "함수 내부 local_counter: $local_counter"
# 전역 변수는 접근 가능
echo "함수 내부에서 본 global_counter: $global_counter"
}
increment_global # global_counter: 1
increment_global # global_counter: 2
echo "함수 밖 global_counter: $global_counter" # 2
use_local # local_counter: 1
use_local # local_counter: 1 (매번 초기화됨)
echo "함수 밖 local_counter: $local_counter" # 빈 값 (지역 변수는 함수 밖에서 접근 불가)
설명
이것이 하는 일: 변수의 범위(scope)를 제어하여 함수 간 변수 충돌을 방지하고 코드의 독립성을 보장합니다. 첫 번째로, 전역 변수는 스크립트 어디서든 선언할 수 있고 어디서든 접근할 수 있습니다.
"global_counter=0"처럼 선언하면 모든 함수가 이 변수를 읽고 수정할 수 있습니다. 이는 여러 함수가 공유해야 하는 상태를 관리할 때 유용하지만, 의도하지 않은 수정이 발생할 위험도 있습니다.
두 번째로, 지역 변수는 "local variable_name=value" 형태로 함수 내부에서만 선언할 수 있습니다. 지역 변수는 함수가 실행되는 동안만 존재하고, 함수가 끝나면 자동으로 메모리에서 제거됩니다.
같은 이름의 전역 변수가 있어도 함수 내부에서는 지역 변수가 우선됩니다. 세 번째로, 함수 내에서 local 없이 변수를 선언하면 전역 변수가 됩니다.
이는 많은 초보자들이 놓치는 부분으로, 함수 안에서 선언했다고 해서 자동으로 지역 변수가 되는 것이 아닙니다. Python이나 JavaScript와 달리 명시적으로 local을 붙여야 합니다.
네 번째로, 중첩된 함수 호출에서는 가장 가까운 범위의 변수가 사용됩니다. 함수 A가 함수 B를 호출하고 양쪽 모두 같은 이름의 지역 변수를 가지면, 각 함수는 자신의 지역 변수를 독립적으로 사용합니다.
여러분이 이 방법을 사용하면 함수를 블랙박스처럼 독립적으로 만들 수 있고, 변수 이름 충돌 걱정 없이 코드를 작성할 수 있으며, 디버깅이 훨씬 쉬워집니다. 특히 여러 개발자가 협업하는 대규모 스크립트에서는 local 사용이 필수입니다.
실전 팁
💡 함수 내부에서 사용하는 모든 임시 변수는 습관적으로 local을 붙이세요. 나중에 전역 변수와 충돌하는 것을 방지할 수 있습니다.
💡 함수의 매개변수도 지역 변수로 선언하면 원본 값을 보호할 수 있습니다. "local param=$1" 형태로 복사해서 사용하세요.
💡 전역 변수는 스크립트 최상단에 모아서 선언하고 주석을 달아두면 관리가 쉽습니다. 어떤 변수가 전역인지 한눈에 파악할 수 있습니다.
💡 함수의 반환값을 전역 변수에 저장하는 것보다 echo로 출력하고 $()로 받는 방식이 더 깔끔합니다. "result=$(my_function)" 형태를 선호하세요.
💡 ShellCheck 같은 정적 분석 도구를 사용하면 local을 빠뜨린 변수를 자동으로 찾아줍니다. 개발 환경에 통합하면 코드 품질이 향상됩니다.
4. 환경변수와 export
시작하며
여러분이 셸 스크립트에서 변수를 설정했는데 그 스크립트가 실행하는 다른 프로그램에서는 그 변수를 인식하지 못하는 상황을 겪어본 적 있나요? 예를 들어 데이터베이스 연결 정보를 변수로 설정했는데 Python 스크립트에서는 그 값을 읽지 못하는 경우입니다.
이런 문제는 일반 셸 변수와 환경변수의 차이를 이해하지 못해서 발생합니다. 일반 변수는 현재 셸 프로세스에서만 유효하고, 자식 프로세스(실행하는 다른 프로그램)에는 전달되지 않습니다.
바로 이럴 때 필요한 것이 export 명령어입니다. 변수를 환경변수로 만들어 자식 프로세스에도 전달할 수 있게 해줍니다.
개요
간단히 말해서, export는 셸 변수를 환경변수로 만들어 자식 프로세스가 상속받을 수 있게 하는 명령어입니다. 환경변수가 필요한 이유는 프로그램 간에 설정값을 전달하기 위해서입니다.
예를 들어, API 키, 데이터베이스 연결 정보, 언어 설정 같은 값들을 환경변수로 관리하면 여러 프로그램이 일관된 설정을 공유할 수 있습니다. 전통적으로는 설정 파일을 읽어야 했다면, 이제는 환경변수를 통해 더 간편하고 표준화된 방식으로 설정을 전달할 수 있습니다.
환경변수의 핵심 특징은 첫째, export로 선언된 변수만 자식 프로세스에 전달됩니다. 둘째, 대문자로 이름을 짓는 것이 관례입니다.
셋째, 한 번 export하면 해당 셸 세션에서 계속 환경변수로 유지됩니다. 이러한 특징들을 이해하면 프로세스 간 통신과 설정 관리가 훨씬 효율적이 됩니다.
코드 예제
#!/bin/bash
# 일반 셸 변수 (자식 프로세스에 전달 안 됨)
NORMAL_VAR="I am normal"
# 환경변수로 export (자식 프로세스에 전달됨)
export ENV_VAR="I am exported"
# 선언과 동시에 export
export DATABASE_URL="postgresql://localhost/mydb"
export API_KEY="secret_key_123"
# 이미 있는 변수를 나중에 export
CONFIG_PATH="/etc/myapp"
export CONFIG_PATH
# 환경변수 확인
echo "현재 셸에서: NORMAL_VAR=$NORMAL_VAR, ENV_VAR=$ENV_VAR"
# 자식 프로세스에서 환경변수 테스트
bash -c 'echo "자식 프로세스: NORMAL_VAR=$NORMAL_VAR, ENV_VAR=$ENV_VAR"'
# 결과: NORMAL_VAR은 빈 값, ENV_VAR은 값이 있음
# Python 스크립트에서 환경변수 사용 예시
python3 -c "import os; print(f'Python에서: {os.getenv(\"ENV_VAR\")}')"
설명
이것이 하는 일: export는 변수를 현재 프로세스뿐만 아니라 이 프로세스가 실행하는 모든 자식 프로세스에서도 접근 가능하게 만듭니다. 첫 번째로, 일반 셸 변수는 현재 셸 세션에서만 유효합니다.
"NORMAL_VAR="value""처럼 선언하면 그 셸에서만 사용할 수 있고, 그 셸이 실행하는 다른 프로그램이나 스크립트에서는 접근할 수 없습니다. 이는 프로세스 격리를 위한 의도된 동작입니다.
두 번째로, "export VAR="value"" 형태로 선언하면 그 변수는 환경변수가 되어 자식 프로세스로 복사됩니다. 중요한 점은 "복사"된다는 것입니다.
자식 프로세스가 환경변수 값을 변경해도 부모 프로세스의 값은 영향을 받지 않습니다. 각 프로세스는 독립적인 환경변수 복사본을 가집니다.
세 번째로, 환경변수는 관례적으로 대문자와 밑줄을 사용하여 이름을 짓습니다. PATH, HOME, USER 같은 시스템 환경변수들이 모두 대문자인 이유입니다.
이는 일반 셸 변수와 시각적으로 구분하기 위한 관습입니다. 네 번째로, .bashrc나 .bash_profile 같은 셸 설정 파일에 export 문을 추가하면 로그인할 때마다 자동으로 환경변수가 설정됩니다.
이는 개발 환경 설정이나 프로젝트별 설정을 영구적으로 관리하는 표준 방법입니다. 다섯 번째로, "export -p" 명령어로 현재 설정된 모든 환경변수를 확인할 수 있고, "unset VAR" 명령어로 환경변수를 제거할 수 있습니다.
여러분이 이 방법을 사용하면 Docker 컨테이너 설정, CI/CD 파이프라인, 서버 배포 스크립트 등에서 일관된 방식으로 설정을 관리할 수 있습니다. 특히 민감한 정보(API 키, 비밀번호)를 코드에 하드코딩하지 않고 환경변수로 관리하는 것은 보안의 기본입니다.
실전 팁
💡 환경변수는 대문자로, 일반 변수는 소문자로 명명하면 코드에서 한눈에 구분할 수 있습니다. 예: export DATABASE_URL vs local db_name
💡 민감한 정보는 절대 스크립트에 하드코딩하지 말고 환경변수로 관리하세요. Git에 실수로 커밋되는 것을 방지할 수 있습니다.
💡 .env 파일을 사용하여 환경변수를 관리하면 편리합니다. "source .env" 명령으로 한 번에 여러 환경변수를 로드할 수 있습니다.
💡 환경변수가 설정되지 않았을 때를 대비해 기본값을 제공하세요. "${DATABASE_URL:-postgresql://localhost/default}" 형태로 안전장치를 마련할 수 있습니다.
💡 프로덕션 환경에서는 systemd 서비스 파일이나 Docker Compose 파일에서 환경변수를 정의하는 것이 일반적입니다. 각 환경마다 다른 설정을 쉽게 적용할 수 있습니다.
5. 특수 변수들
시작하며
여러분이 셸 스크립트에 전달된 인자들을 처리하려고 하는데 "$1, $2, $3..." 같은 이상한 변수들이 무엇인지 혼란스러웠던 적 있나요? 또는 "이 스크립트가 몇 개의 인자를 받았는지 어떻게 확인하지?"라고 고민해본 경험이 있을 것입니다.
이런 상황은 명령행 인자를 받는 스크립트를 작성할 때 항상 마주치는 문제입니다. 사용자가 입력한 값들을 제대로 처리하지 못하면 스크립트의 유용성이 크게 떨어집니다.
바로 이럴 때 필요한 것이 Bash의 특수 변수들입니다. $0, $1, $#, $@, $* 같은 특수 변수들을 이해하면 명령행 인자를 자유자재로 다룰 수 있습니다.
개요
간단히 말해서, Bash는 스크립트 실행 정보와 인자를 담는 특수한 변수들을 자동으로 제공합니다. 특수 변수가 필요한 이유는 스크립트 실행 컨텍스트를 파악하고 사용자 입력을 처리하기 위해서입니다.
예를 들어, 파일 처리 스크립트를 만들 때 사용자가 어떤 파일들을 인자로 전달했는지 알아야 합니다. 전통적으로는 인자를 일일이 확인해야 했다면, 이제는 특수 변수를 통해 자동으로 제공되는 정보를 활용할 수 있습니다.
특수 변수의 핵심 특징은 첫째, 읽기 전용으로 값을 변경할 수 없습니다. 둘째, Bash가 자동으로 설정하므로 선언할 필요가 없습니다.
셋째, 각각 고유한 의미와 용도를 가지고 있습니다. 이러한 특징들을 이해하면 강력한 명령행 도구를 만들 수 있습니다.
코드 예제
#!/bin/bash
# $0: 스크립트 이름 또는 셸 이름
echo "스크립트 이름: $0"
# $1, $2, ...: 위치 매개변수 (명령행 인자)
echo "첫 번째 인자: $1"
echo "두 번째 인자: $2"
# $#: 전달된 인자의 개수
echo "총 인자 개수: $#"
# $@: 모든 인자를 개별적으로 (큰따옴표로 감싸면 각각 분리)
echo "모든 인자 (@): $@"
for arg in "$@"; do
echo " - $arg"
done
# $*: 모든 인자를 하나의 문자열로
echo "모든 인자 (*): $*"
# $?: 마지막 명령의 종료 상태 (0=성공, 그 외=실패)
ls /existing_dir
echo "ls 명령 종료 코드: $?"
ls /nonexistent_dir 2>/dev/null
echo "실패한 ls 명령 종료 코드: $?"
# $$: 현재 프로세스 ID
echo "현재 스크립트 PID: $$"
# $!: 백그라운드로 실행된 마지막 프로세스 ID
sleep 10 &
echo "백그라운드 프로세스 PID: $!"
설명
이것이 하는 일: 특수 변수는 스크립트 실행 환경과 인자에 대한 중요한 정보를 자동으로 제공합니다. 첫 번째로, $0는 스크립트의 이름(경로 포함)을 담고 있습니다.
"bash script.sh" 형태로 실행하면 $0은 "script.sh"가 됩니다. 이는 스크립트가 어떤 이름으로 호출되었는지 확인하는 데 유용하며, 사용법 메시지를 출력할 때 "$0 [options]" 형태로 자주 사용됩니다.
두 번째로, $1, $2, $3 등의 위치 매개변수는 스크립트에 전달된 인자들을 순서대로 담고 있습니다. "script.sh file.txt backup" 형태로 실행하면 $1은 "file.txt", $2는 "backup"이 됩니다.
9개 이상의 인자는 ${10}, ${11} 형태로 중괄호를 사용해야 합니다. 세 번째로, $#은 전달된 인자의 개수를 담고 있어서 인자 검증에 매우 유용합니다.
"if [ $# -lt 2 ]; then echo 'Usage: $0 <source> <dest>'; exit 1; fi" 같은 패턴으로 필수 인자를 강제할 수 있습니다. 네 번째로, $@와 $*는 모든 인자를 나타내지만 미묘한 차이가 있습니다.
"$@"는 각 인자를 개별적으로 유지하므로 "for arg in "$@"" 루프에 적합합니다. "$*"는 모든 인자를 하나의 문자열로 합치므로 로깅이나 출력에 적합합니다.
다섯 번째로, $?는 마지막으로 실행된 명령의 종료 코드를 담고 있습니다. 0은 성공, 1-255는 다양한 에러를 의미합니다.
이를 활용하면 "if ! command; then handle_error; fi" 형태로 에러 처리를 할 수 있습니다.
여섯 번째로, $$는 현재 스크립트의 프로세스 ID로, 임시 파일 이름을 만들 때 유용합니다. "/tmp/myapp.$$"처럼 사용하면 동시 실행되는 스크립트들이 서로 다른 임시 파일을 사용합니다.
여러분이 이 방법을 사용하면 사용자 친화적인 CLI 도구를 만들 수 있고, 에러를 적절히 처리하며, 여러 파일을 한 번에 처리하는 유연한 스크립트를 작성할 수 있습니다.
실전 팁
💡 스크립트 시작 부분에서 인자 개수를 검증하세요. "[ $# -eq 0 ] && { echo 'Usage: $0 <file>'; exit 1; }" 형태로 필수 인자를 강제할 수 있습니다.
💡 "$@"를 사용할 때는 반드시 큰따옴표로 감싸세요. 공백이 포함된 인자를 올바르게 처리할 수 있습니다.
💡 함수에서도 동일한 특수 변수를 사용할 수 있습니다. 함수 내부의 $1, $2는 함수에 전달된 인자를 의미합니다.
💡 종료 코드 $?를 활용하여 파이프라인의 각 단계를 검증하세요. 명령 실패 시 적절한 에러 메시지와 함께 스크립트를 중단할 수 있습니다.
💡 shift 명령으로 위치 매개변수를 이동시킬 수 있습니다. "shift"를 실행하면 $2가 $1이 되고, $3이 $2가 되는 식으로 while 루프에서 인자를 순차 처리할 때 유용합니다.
6. readonly와 unset
시작하며
여러분이 중요한 설정값을 변수에 저장했는데 실수로 스크립트 중간에서 그 값을 덮어써서 전체 시스템이 잘못된 설정으로 동작한 경험이 있나요? 또는 더 이상 필요 없는 변수를 메모리에서 제거하고 싶었던 적이 있을 것입니다.
이런 문제는 변수의 생명주기와 불변성을 제대로 관리하지 못해서 발생합니다. 특히 긴 스크립트에서는 어디선가 실수로 중요한 변수를 수정하는 것을 방지하기 어렵습니다.
바로 이럴 때 필요한 것이 readonly와 unset 명령어입니다. readonly는 변수를 상수로 만들어 보호하고, unset은 변수를 완전히 제거하여 메모리를 관리합니다.
개요
간단히 말해서, readonly는 변수를 읽기 전용으로 만들어 변경을 방지하고, unset은 변수를 삭제합니다. 이 명령어들이 필요한 이유는 변수의 불변성을 보장하고 메모리를 효율적으로 관리하기 위해서입니다.
예를 들어, API 엔드포인트나 데이터베이스 접속 정보 같은 상수는 readonly로 보호하면 실수로 변경되는 것을 막을 수 있습니다. 전통적으로는 변수 이름을 대문자로 써서 "건드리지 말라"는 암묵적 신호를 보냈다면, 이제는 readonly로 명시적으로 보호할 수 있습니다.
이 명령어들의 핵심 특징은 첫째, readonly 변수는 unset으로도 삭제할 수 없습니다. 둘째, 함수 내부의 local readonly 변수는 함수가 끝나면 소멸됩니다.
셋째, unset은 변수뿐만 아니라 함수도 제거할 수 있습니다. 이러한 특징들을 이해하면 더 안전하고 효율적인 스크립트를 작성할 수 있습니다.
코드 예제
#!/bin/bash
# readonly 변수 선언 방법 1: 선언과 동시에
readonly API_ENDPOINT="https://api.example.com"
# readonly 변수 선언 방법 2: 나중에 readonly로 지정
DATABASE_HOST="localhost"
readonly DATABASE_HOST
# 설정 상수들을 readonly로 보호
readonly MAX_RETRIES=3
readonly TIMEOUT=30
echo "API Endpoint: $API_ENDPOINT"
# readonly 변수 수정 시도 (에러 발생)
# API_ENDPOINT="https://api.different.com" # bash: API_ENDPOINT: readonly variable
# 일반 변수
temp_file="/tmp/data.txt"
echo "Temp file: $temp_file"
# 변수 삭제
unset temp_file
echo "After unset: $temp_file" # 빈 값
# readonly 변수는 unset 불가
# unset API_ENDPOINT # bash: unset: API_ENDPOINT: cannot unset: readonly variable
# readonly 변수 목록 확인
echo "=== Readonly 변수들 ==="
readonly -p | grep API_ENDPOINT
# 함수에서 local readonly 사용
function configure() {
local readonly CONFIG_FILE="/etc/app.conf"
echo "Config: $CONFIG_FILE"
# CONFIG_FILE="/other/path" # 에러 발생
}
configure
설명
이것이 하는 일: readonly는 변수를 불변(immutable)으로 만들어 의도하지 않은 수정을 컴파일 시점에 방지하고, unset은 변수를 완전히 제거합니다. 첫 번째로, readonly로 선언된 변수는 단 한 번만 값을 할당받을 수 있고 이후에는 절대 변경할 수 없습니다.
"readonly VAR=value" 형태로 선언과 동시에 지정하거나, 일반 변수를 선언한 후 "readonly VAR" 형태로 나중에 읽기 전용으로 만들 수 있습니다. 수정을 시도하면 "readonly variable" 에러가 발생하며 스크립트는 계속 실행됩니다(중단되지 않음).
두 번째로, readonly는 설정값, API 키, 버전 정보 같은 스크립트 실행 중 절대 바뀌지 말아야 할 값들을 보호하는 데 필수적입니다. 특히 여러 개발자가 협업하는 환경에서 "이 변수는 절대 변경하면 안 된다"는 의도를 코드로 명확히 표현할 수 있습니다.
세 번째로, unset 명령어는 변수를 완전히 삭제합니다. "unset VAR" 실행 후에는 그 변수가 정의되지 않은 상태가 되어 "$VAR"는 빈 문자열을 반환합니다.
이는 변수를 ""(빈 문자열)로 설정하는 것과 다릅니다. 빈 문자열은 여전히 정의된 변수이지만, unset된 변수는 존재하지 않는 변수입니다.
네 번째로, unset은 대용량 데이터를 담은 변수나 배열을 제거하여 메모리를 확보할 때 유용합니다. 긴 시간 동안 실행되는 스크립트에서는 더 이상 사용하지 않는 변수를 명시적으로 unset하는 것이 좋은 습관입니다.
다섯 번째로, readonly 변수는 unset으로도 제거할 수 없습니다. 이는 강력한 보호 메커니즘으로, 실수로라도 중요한 상수를 삭제하는 것을 방지합니다.
readonly 변수는 셸 세션이 종료될 때까지 유지됩니다. 여섯 번째로, 함수 내부에서 "local readonly VAR=value" 형태로 선언하면 함수 범위 내에서만 읽기 전용인 지역 상수를 만들 수 있습니다.
함수가 종료되면 자동으로 소멸됩니다. 여러분이 이 방법을 사용하면 설정값 보호, 실수 방지, 메모리 관리를 효과적으로 할 수 있으며, 코드의 의도를 더 명확히 표현할 수 있습니다.
특히 프로덕션 환경에서 실행되는 중요한 스크립트에서는 readonly 사용이 필수적입니다.
실전 팁
💡 스크립트 상단에 모든 설정 상수를 readonly로 선언하는 섹션을 만드세요. 한눈에 어떤 값들이 고정되어 있는지 파악할 수 있습니다.
💡 readonly 변수는 관례적으로 대문자와 밑줄을 사용하여 명명하세요. MAX_RETRY_COUNT, API_BASE_URL처럼 상수임을 시각적으로 표현할 수 있습니다.
💡 배열도 readonly로 선언할 수 있습니다. "readonly -a ALLOWED_USERS=(alice bob charlie)" 형태로 허용 목록 같은 고정 데이터를 보호하세요.
💡 "set -u" 옵션과 함께 사용하면 정의되지 않은 변수 참조를 에러로 처리할 수 있습니다. unset 후 실수로 변수를 사용하는 것을 방지합니다.
💡 "readonly -p" 명령으로 현재 셸의 모든 readonly 변수를 확인할 수 있습니다. 디버깅 시 어떤 변수가 읽기 전용인지 빠르게 파악할 수 있습니다.
댓글 (0)
함께 보면 좋은 카드 뉴스
Terraform 변수와 출력값 완벽 가이드
Terraform의 입력 변수와 출력값을 활용하여 재사용 가능하고 유연한 인프라 코드를 작성하는 방법을 배웁니다. 변수 타입, 검증, 파일 분리부터 민감한 출력값 처리까지 실무에 필요한 모든 내용을 다룹니다.
Client VPN으로 프라이빗 리소스 접근하기
AWS Client VPN을 활용하여 프라이빗 서브넷의 RDS, EC2 등 내부 리소스에 안전하게 접근하는 방법을 단계별로 알아봅니다. 인증서 생성부터 실제 연결까지 실무에서 바로 적용할 수 있는 완벽 가이드입니다.
NAT 게이트웨이로 프라이빗 서브넷 인터넷 연결 완벽 가이드
AWS VPC의 프라이빗 서브넷에서 인터넷에 접근하는 방법을 NAT 게이트웨이를 통해 배웁니다. 보안을 유지하면서 외부 API 호출, 패키지 업데이트 등을 수행하는 핵심 네트워크 아키텍처를 쉽게 이해할 수 있습니다.
Terminal과 Shell 도구 완벽 가이드
프로그래밍에서 터미널과 셸을 다루는 방법을 배웁니다. 명령 실행부터 출력 스트리밍, 타임아웃 관리까지 실무에서 바로 활용할 수 있는 핵심 기법을 다룹니다.
Kurtosis 기본 명령어 마스터
분산 시스템과 블록체인 노드를 격리된 환경에서 손쉽게 테스트할 수 있는 Kurtosis의 핵심 명령어를 다룹니다. 초급 개발자도 바로 실무에 적용할 수 있도록 각 명령어의 개념과 활용법을 친절하게 설명합니다.