가이드

초보자를 위한 bash 스크립트: 첫 스크립트, 변수, 조건, 반복문, cron

업데이트: · Skillaria.Top

bash 스크립트는 #!/bin/bash 줄로 시작하고, chmod +x로 실행 권한을 받아 ./script.sh 형태로 실행하는 셸 명령 모음 텍스트 파일입니다. 스크립트에서는 변수, $1과 $2 인자, if 조건, for와 while 반복문을 사용하며, 정기 실행은 crontab -e로 설정합니다.

bash 스크립트는 터미널에 하나씩 입력했을 명령들을 모아 둔 평범한 텍스트 파일입니다. 스크립트는 #!/bin/bash shebang 줄로 시작하고, chmod +x로 실행 권한을 얻으며, ./script.sh처럼 실행합니다. 이 글은 초보자를 위해 첫 줄부터 주기적 실행까지, 즉 변수, 인자, 조건, 반복문, 오류 처리, cron을 차례로 설명합니다.

1분 만에 만드는 첫 bash 스크립트

아무 편집기에서나 파일을 만드세요. 예를 들어 nano를 사용합니다:

nano hello.sh
#!/bin/bash
# 첫 스크립트: 인사와 시스템 정보
echo "안녕하세요, $USER!"
echo "오늘: $(date +%Y-%m-%d)"
echo "현재 위치: $(pwd)"

저장한 뒤(Ctrl+O, Enter, Ctrl+X) 실행 권한을 주고 실행하세요:

$ chmod +x hello.sh
$ ./hello.sh
안녕하세요, alice!
오늘: 2026-10-12
현재 위치: /home/alice

#로 시작하는 줄은 주석입니다. $(명령)은 명령의 출력을 줄 안에 그대로 넣습니다.

Shebang: 첫 줄이 중요한 이유

#!/bin/bash 줄은 시스템에 이 파일을 어떤 인터프리터로 실행할지 알려 줍니다. 이 줄이 없으면 스크립트는 그때 찾을 수 있는 셸로 실행됩니다. 일부 시스템에서 /bin/sh는 bash가 아니라 더 단순한 dash이며, 그곳에서는 일부 문법이 동작하지 않습니다.

자주 보이는 또 다른 형태는 #!/usr/bin/env bash입니다. 이것은 PATH에서 bash를 찾으므로, bash가 /bin에 없을 때 편리합니다.

스크립트 실행 방법과 ./가 필요한 이유

실행 방법은 두 가지입니다:

  • ./hello.sh — shebang과 x 권한이 필요합니다. 현재 디렉터리는 PATH에 없으므로 ./ 접두사가 필수입니다.
  • bash hello.sh — 실행 권한이 필요 없습니다. 인터프리터를 명시하기 때문입니다.

./가 없으면 command not found, chmod +x가 없으면 Permission denied가 나오는데, 이 두 가지가 바로 그 원인입니다. 자신의 스크립트를 어디서든 이름으로 부르고 싶다면 ~/bin이나 /usr/local/bin에 두세요.

변수

변수는 = 주변에 공백 없이 정의하고, $를 붙여 읽습니다:

#!/bin/bash
name="backup"
dir="/var/backups"
today=$(date +%F)

echo "파일: ${dir}/${name}-${today}.tar.gz"

중괄호는 변수 이름을 주변 텍스트와 구분합니다. 공백이 있는 name = "backup"은 잘못된 문장입니다: bash는 name이라는 명령을 실행하려고 합니다.

따옴표가 중요합니다

큰따옴표 안에서는 변수가 치환되지만, 작은따옴표 안에서는 치환되지 않습니다. 변수는 거의 항상 큰따옴표로 감싸세요. 그렇지 않으면 공백이 든 파일 이름이 두 개의 인자로 쪼개집니다.

file="내 보고서.txt"
rm $file      # 잘못됨: «내»와 «보고서.txt»를 지우려고 합니다
rm "$file"    # 올바름
echo '$HOME'  # 그대로 $HOME을 출력
echo "$HOME"  # /home/alice를 출력

환경 변수

$HOME, $USER, $PATH 같은 변수는 시스템이 설정합니다. 자신의 변수는 export로 자식 프로세스에 넘길 수 있습니다. 영구적인 설정은 ~/.bashrc나 ~/.profile에 적습니다.

스크립트 인자

실행할 때 전달한 값은 $1, $2 등으로 받습니다.

ead>
변수내용
$0스크립트 이름
$1, $2…첫 번째, 두 번째 인자
$#인자 개수
"$@"모든 인자를 각각
$?마지막 명령의 종료 코드
$$현재 스크립트의 PID
#!/bin/bash
echo "스크립트 $0 이(가) 인자 $#개를 받았습니다"
echo "첫 번째: $1"
$ ./args.sh nginx 80
스크립트 ./args.sh 이(가) 인자 2개를 받았습니다
첫 번째: nginx

사용자 입력은 read 명령으로 받습니다: read -p "이름을 입력하세요: " name.

if 조건

모든 명령은 종료 코드를 반환합니다. 0은 성공, 그 밖의 값은 실패입니다. if는 바로 이 코드를 검사합니다. 비교에는 [[ ... ]]를 사용하며, 괄호 안쪽의 공백은 필수입니다.

#!/bin/bash
if [[ $# -eq 0 ]]; then
    echo "사용법: $0 <디렉터리>"
    exit 1
fi

if [[ -d "$1" ]]; then
    echo "디렉터리 $1 이(가) 있습니다"
elif [[ -f "$1" ]]; then
    echo "$1 은(는) 디렉터리가 아니라 파일입니다"
else
    echo "$1 을(를) 찾을 수 없습니다"
fi
ead>
검사의미
-f file일반 파일이 존재함
-d dir디렉터리가 존재함
-e path경로가 존재함
-x file파일을 실행할 수 있음
-z "$var" / -n "$var"문자열이 비어 있음 / 비어 있지 않음
"$a" == "$b", !=문자열 비교
-eq -ne -lt -le -gt -ge숫자 비교

임의의 명령을 직접 조건으로 쓸 수도 있습니다:

if systemctl is-active --quiet nginx; then
    echo "nginx가 실행 중입니다"
else
    echo "nginx가 중지되어 있습니다"
fi

선택지가 많을 때는 case가 편리합니다:

case "$1" in
    start)   echo "시작합니다" ;;
    stop)    echo "중지합니다" ;;
    *)       echo "알 수 없는 명령: $1"; exit 1 ;;
esac

for와 while 반복문

for: 목록 순회

for host in web1 web2 db1; do
    echo "$host 확인 중"
    ping -c 1 -W 1 "$host" > /dev/null && echo "  응답함" || echo "  응답 없음"
done

for file in /var/log/*.log; do
    echo "$file: $(wc -l < "$file")줄"
done

for i in {1..5}; do
    echo "시도 $i"
done

while: 조건이 참인 동안

파일을 한 줄씩 읽는 대표적인 예:

while read -r user; do
    echo "사용자: $user"
done < users.txt

서비스가 올라올 때까지 기다리기:

count=0
until curl -sf http://localhost:8080/health > /dev/null; do
    count=$((count + 1))
    if [[ $count -ge 10 ]]; then
        echo "서비스가 올라오지 않았습니다"
        exit 1
    fi
    sleep 3
done
echo "서비스 준비 완료"

bash의 산술 연산은 이중 괄호로 씁니다: $((a + b)). 정수만 다룹니다.

함수

반복되는 코드는 함수로 묶습니다. 함수 안의 인자도 $1, $2이며, local은 변수가 밖으로 새어 나가지 않게 합니다.

log() {
    local level="$1"; shift
    echo "$(date '+%F %T') [$level] $*"
}

log INFO "백업을 시작합니다"
log ERROR "디스크 공간이 부족합니다"

오류 처리: set -euo pipefail

기본적으로 bash는 실패한 명령 뒤에도 계속 실행됩니다. 그래서 백업 스크립트가 아무것도 복사하지 않고도 «성공»으로 끝날 수 있습니다. 그래서 스크립트 맨 위에는 엄격 모드를 씁니다:

#!/bin/bash
set -euo pipefail
  • -e — 첫 오류에서 멈춥니다.
  • -u — 선언되지 않은 변수를 쓰면 오류로 봅니다 (DIR이 비어 있을 때 rm -rf "$DIR/"가 실행되는 것을 막습니다).
  • -o pipefail — 파이프의 어느 한 부분이라도 실패하면 파이프 전체를 실패로 봅니다.

디버깅하려면 bash -x script.sh로 실행하세요. bash가 명령을 실행하기 전에 하나씩 출력합니다. 실행 전에 흔한 실수를 찾고 싶다면 shellcheck가 도움이 됩니다.

실전 예제: 백업 스크립트

#!/bin/bash
set -euo pipefail

SRC="${1:-/var/www}"
DST="/var/backups/site"
KEEP_DAYS=7
STAMP=$(date +%F_%H-%M)

mkdir -p "$DST"
tar -czf "$DST/site-$STAMP.tar.gz" -C "$(dirname "$SRC")" "$(basename "$SRC")"
echo "아카이브 생성: $DST/site-$STAMP.tar.gz"

# KEEP_DAYS일보다 오래된 아카이브 삭제
find "$DST" -name "site-*.tar.gz" -mtime +"$KEEP_DAYS" -delete

${1:-/var/www}은 «첫 번째 인자, 없으면 /var/www»라는 뜻입니다. 스크립트를 /usr/local/bin/backup-site.sh로 저장하고 실행 가능하게 만드세요: sudo chmod +x /usr/local/bin/backup-site.sh.

주기적 실행: cron

cron은 정해진 시각에 명령을 실행하는 서비스입니다. 사용자 작업은 crontab -e로 편집하고 crontab -l로 확인합니다. root의 작업은 sudo crontab -e로 편집합니다.

cron 한 줄은 시간 필드 다섯 개와 명령으로 이루어집니다:

# 분 시 일 월 요일 명령
30 2 * * * /usr/local/bin/backup-site.sh >> /var/log/backup.log 2>&1
ead>
일정실행 시점
*/5 * * * *5분마다
0 * * * *매시 정각
30 2 * * *매일 02:30
0 9 * * 1-5평일 09:00
0 3 1 * *매월 1일 03:00
@reboot부팅 후 한 번

손으로는 되는데 cron에서는 왜 안 될까

  • 다른 환경. cron 작업은 최소한의 PATH로, ~/.bashrc 없이 실행됩니다. 명령과 파일의 전체 경로를 쓰거나, crontab 맨 위에서 PATH를 설정하세요.
  • 다른 현재 디렉터리. 작업은 홈 디렉터리에서 시작하며, 스크립트가 있는 곳에서 시작하지 않습니다. 절대 경로를 사용하세요.
  • % 기호. crontab에서 %는 줄바꿈을 뜻합니다. 그래서 작업 줄 안의 date +%F는 \%로 이스케이프해야 합니다. 더 간단한 방법은 명령을 스크립트 안으로 옮기는 것입니다.
  • 출력이 사라짐. 출력을 로그 파일로 보내세요 (>> file 2>&1). 그렇지 않으면 오류를 볼 수 없습니다. cron 실행 기록은 journalctl -u cron으로 확인합니다 (RHEL 계열에서는 서비스 이름이 crond입니다).

systemd를 쓰는 시스템에서는 cron 대신 systemd 타이머를 쓸 수 있습니다. 타이머는 출력을 저널에 남기고, 놓친 실행을 나중에 보충할 수 있습니다.

마무리

초보자를 위한 bash 스크립트는 shebang #!/bin/bash, 실행 권한, 명령들의 순서로 이루어진 파일입니다. 따옴표로 감싼 변수, if로 인자 검사, for와 while 반복문, 그리고 엄격 모드 set -euo pipefail을 더하면, cron이 정해진 시각에 실행해 주는 믿을 만한 자동화 도구가 됩니다. bash 스크립트, 환경 변수, cron은 강의에서 실습과 함께 다룹니다.

자주 묻는 질문

bash와 sh는 어떻게 다른가요?

sh는 표준 POSIX 셸이고, bash는 배열, 이중 괄호 등 편의 기능을 더한 확장판입니다. bash 문법을 쓴 스크립트는 sh가 아니라 bash로 실행해야 합니다.

bash 스크립트의 확장자는 무엇이어야 하나요?

리눅스는 확장자를 보지 않으며, 실행 인터프리터는 shebang으로 정해집니다. <code>.sh</code>는 사람이 스크립트임을 알아보기 위한 관례일 뿐입니다.

스크립트에 비밀번호를 안전하게 전달하려면?

인자로 넘기지 마세요. 인자는 <code>ps</code>로 다른 사용자에게도 보입니다. 600 권한의 파일에서 환경 변수를 읽거나, <code>read -s</code>로 화면에 표시하지 않고 입력받으세요.

스크립트가 root로 실행되었는지 어떻게 확인하나요?

사용자 ID를 0과 비교하세요: <code>if [[ $EUID -ne 0 ]]; then echo "root 권한이 필요합니다"; exit 1; fi</code>.

SSH를 끊은 뒤에도 스크립트가 계속 돌게 하려면?

<code>nohup ./script.sh &</code>를 쓰거나 tmux, screen 세션에서 실행하세요. 계속 돌려야 한다면 스크립트를 systemd 서비스로 만드세요.

영상으로 이 주제 배우기

이 주제는 “Linux: с нуля до уверенного администрирования” 강의에서 실습과 함께 자세히 다룹니다.

강의로 이동