공공부하자개발 · 영어 학습 노트
리눅스
리눅스 운영RHEL 에서 배포·운영하기0/13 완료
  • 01셸 기본과 파일 시스템
  • 02텍스트 처리 (grep·sed·awk)
  • 03프로세스·서비스 관리 (systemd)
  • 04자바 애플리케이션 배포
  • 05Tomcat·nginx 배포·재기동
  • 06로그·디스크 운영 (logrotate·cron)
  • 07네트워크 진단 (ss·curl·firewalld)
  • 08셸 스크립트 (운영 자동화)
  • 09장애 대응 (jstack·jmap·OOM killer·FD)
  • 10보안 기초 (ssh 강화·SELinux·감사 로그)
  • 11컨테이너 기초 (podman)
  • 12백업·복구 (설정·릴리스·DB 덤프)
  • 13Docker 실무
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 리눅스 운영 › 08 / 13

셸 스크립트 (운영 자동화)

섹션 7진행 0 / 13
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6연습 문제7정리‹ 이전다음 ›

2. 핵심 원리

2.1 첫 세 줄

모든 운영 스크립트는 같은 세 줄로 시작합니다.

bash
#!/usr/bin/env bash
set -euo pipefail
trap 'rm -rf "$WORK"' EXIT
옵션 뜻
-e 명령 실패 시 즉시 종료
-u 정의 안 된 변수 사용 시 종료
-o pipefail 파이프 중간 실패도 실패

-e 가 없으면 cd /없는폴더 가 실패해도 다음 줄이 실행됩니다. -u 는 rm -rf "$DIR/" 에서 DIR 오타를 잡습니다. pipefail 은 grep | sort 에서 grep 이 실패해도 sort 가 성공하면 성공으로 보는 것을 막습니다.

2.2 인자와 옵션

$1, $2 가 위치 인자, $# 가 개수, $@ 가 전부입니다. 옵션은 while 과 case 로 처리하고 shift 로 하나씩 소비합니다.

bash
DRY_RUN=0
while [ $# -gt 0 ]; do
  case $1 in
    --dry-run) DRY_RUN=1 ;;
    -h|--help) usage ;;
    -*) echo "모르는 옵션: $1"; usage ;;
    *) break ;;
  esac
  shift
done
LOG_DIR=${1:-/opt/myapp/logs}

옵션을 다 먹은 뒤 남은 $1 이 위치 인자입니다. ${1:-기본값} 으로 없을 때 기본값을 줍니다. ${1:?메시지} 는 없으면 메시지를 내고 종료합니다.

2.3 함수와 종료 코드

함수는 이름() { } 으로 정의하고 인자는 $1 로 받습니다. return 은 함수 종료 코드, exit 는 스크립트 종료입니다.

bash
log()  { printf '%s [%s] %s\n' "$(date '+%F %T')" "$1" "$2"; }
info() { log INFO "$1"; }
die()  { log ERROR "$1" >&2; exit "${2:-1}"; }
run()  { if [ "$DRY_RUN" = 1 ]; then echo "  [dry] $*"; else "$@"; fi; }

die 는 에러를 표준 에러로 내고 종료 코드를 정합니다. run 은 dry run 이면 명령을 출력만 합니다. 이 네 함수를 파일 앞에 두면 나머지가 읽기 쉬워집니다.

2.4 조건: [ ] 와 [[ ]]

[ ] 는 test 명령이고 [[ ]] 는 bash 확장입니다. 파일·숫자 검사는 둘 다 되고 패턴·정규식은 [[ ]] 만 됩니다.

검사 문법
파일 존재 [ -f "$f" ]
폴더 존재 [ -d "$d" ]
비어 있지 않음 [ -s "$f" ]
문자열 비었나 [ -z "$s" ]
숫자 비교 [ "$n" -gt 3 ]
문자열 비교 [ "$a" = "$b" ]
패턴 [[ $f == *.log ]]
정규식 [[ $s =~ ^[0-9]+$ ]]

숫자는 -eq -ne -gt -lt, 문자열은 = != 입니다. [ ] 안에서는 변수를 꼭 따옴표로 감쌉니다. [[ ]] 는 감싸지 않아도 되지만 습관은 통일합니다.

2.5 반복

bash
for f in "$LOG_DIR"/*.log; do echo "$f"; done          # 글로브
for i in $(seq 1 5); do echo "$i"; done                 # 숫자
while IFS= read -r line; do echo "$line"; done < file   # 줄 단위
while true; do curl -sf URL && break; sleep 1; done     # 조건 될 때까지

파일을 줄 단위로 읽을 때는 while read 이지 for line in $(cat file) 이 아닙니다. 후자는 공백에서 쪼개집니다. read -r 은 백슬래시를 그대로 두고 IFS= 는 앞뒤 공백을 보존합니다.

2.6 변수 확장

문자열 자르기는 외부 명령 없이 확장 문법으로 합니다. basename·dirname 보다 빠르고 서브셸이 없습니다.

문법 뜻 path=/opt/app/a-1.2.jar
${path##*/} 앞에서 긴 */ 제거 a-1.2.jar
${path%/*} 뒤에서 짧은 /* 제거 /opt/app
${path%.jar} 뒤 .jar 제거 /opt/app/a-1.2
${v:-기본} 없으면 기본
${v:?메시지} 없으면 종료
${#v} 길이

# 은 앞, % 는 뒤, 두 번 쓰면 가장 긴 일치입니다. 04 레슨 app.sh 의 ${JAVA_HOME:+$JAVA_HOME/bin/} 는 있을 때만 값을 쓰는 :+ 입니다.

2.7 따옴표 규칙

변수는 항상 큰따옴표로 감쌉니다. 파일명에 공백이 있으면 감싸지 않은 $f 가 두 인자로 쪼개집니다.

bash
cp "$src" "$dst"            # 항상 이렇게
for f in "$DIR"/*; do        # 글로브는 따옴표 밖
echo '$HOME 은 그대로'        # 작은따옴표는 확장 없음
java $JAVA_OPTS -jar app.jar # 단어 분리가 목적일 때만 예외

예외는 JAVA_OPTS 처럼 일부러 여러 인자로 나눌 때뿐입니다. 그 경우에도 배열 "${OPTS[@]}" 가 더 안전합니다.

2.8 명령 치환과 산술

bash
count=$(grep -c ERROR "$f")         # 출력을 변수로
now=$(date +%F)
total=$((count + 10))               # 정수 산술
pct=$(( used * 100 / size ))        # 정수 나눗셈
if grep -q ERROR "$f"; then ...; fi # 종료 코드를 조건으로

백틱 대신 $( ) 를 씁니다. 중첩되고 읽기 쉽습니다. 소수 계산은 awk 나 bc 가 필요합니다.

2.9 set -e 와 조건 문맥

set -e 는 if, while, &&, || 안의 실패에는 반응하지 않습니다. grep 이 못 찾아 1 을 돌려줘도 if grep -q 는 정상 흐름입니다.

bash
grep -q ERROR "$f" || echo "에러 없음"      # 실패해도 계속
set +e; risky_command; rc=$?; set -e       # 종료 코드가 필요할 때

종료 코드를 변수에 담아야 할 때만 잠시 set +e 로 풉니다. cmd || true 는 실패를 무시하는 관용구입니다.

2.10 trap

trap 은 신호나 이벤트에 명령을 겁니다. EXIT 는 정상·비정상 종료 모두에 실행되어 정리에 씁니다.

bash
trap 'rm -rf "$WORK"' EXIT              # 임시 폴더 정리
trap 'echo "줄 $LINENO 실패" >&2' ERR    # 어느 줄에서 죽었나
trap 'echo "중단 요청"; exit 130' INT    # Ctrl+C

03 레슨의 worker.sh 가 TERM 을 잡은 것과 같은 문법입니다. ERR trap 은 set -e 로 종료되기 직전에 실행되어 원인 줄을 알려 줍니다.

2.11 잠금 파일

cron 이 겹치거나 두 사람이 동시에 배포하면 사고입니다. mkdir 는 원자적이라 잠금으로 씁니다.

bash
LOCK=/var/run/myapp-deploy.lock
mkdir "$LOCK" 2>/dev/null || { echo "이미 실행 중"; exit 1; }
trap 'rm -rf "$LOCK"' EXIT

flock 명령이 있으면 flock -n /var/run/deploy.lock 명령 한 줄로도 됩니다. 스크립트가 죽어도 EXIT trap 이 잠금을 지웁니다.

2.12 cron 용 스크립트 체크리스트

항목 이유
절대 경로 cron 의 PATH 가 짧음
cd 명시 실행 위치가 다름
출력을 파일로 메일 대신 로그
입력 없음 대화형 프롬프트 불가
잠금 겹침 방지
종료 코드 0 성공, 나머지 실패

shellcheck 파일.sh 로 정적 검사를 하면 따옴표 누락과 [ ] 실수를 잡아 줍니다. RHEL 은 dnf install ShellCheck, 없으면 PC 에서 돌립니다.

핵심 원리
  • 2.1 첫 세 줄
  • 2.2 인자와 옵션
  • 2.3 함수와 종료 코드
  • 2.4 조건: [ ] 와 [[ ]]
  • 2.5 반복
  • 2.6 변수 확장
  • 2.7 따옴표 규칙
  • 2.8 명령 치환과 산술
  • 2.9 set -e 와 조건 문맥
  • 2.10 trap
  • 2.11 잠금 파일
  • 2.12 cron 용 스크립트 체크리스트
이전 섹션1 왜 배우는가2 / 7다음 섹션3 코드 예제