Step 28. bash 스크립트와 cron — 손으로 치던 명령을 자명종에 맡겨라
Level 0 — 컴퓨터 조작과 구조의 이해 | 난이도 ★★☆☆☆ | 예상 소요 시간 3시간
전제: Step 18~27(리눅스 기본기, 권한, 프로세스, 디렉토리 구조)을 끝냈어야 합니다. 우분투 가상머신(또는 WSL 우분투)이 필요합니다.
- 준비물: 우분투 터미널과 nano 에디터. 새로 설치하는 것은 없습니다.
- 주의: 오늘 만드는 것은 내 홈 폴더의 작은 텍스트 파일들뿐이라 안전합니다. 단 하나의 철칙 — cron에 등록했으면 끝날 때 반드시 해제합니다. 켜 두고 잊으면 로그 파일이 계속 불어납니다.
지금까지 우리는 명령을 한 줄씩 손으로 쳤습니다. 그런데 매일 아침 "디스크 확인하고, 메모리 확인하고, 로그 백업하고"를 손으로 친다면 사흘이면 지칠 겁니다. 컴퓨터는 원래 반복을 대신하라고 만든 기계입니다. 명령어들을 파일에 적어 두고 통째로 실행시키는 스크립트(script, 대본)와, 그 스크립트를 정해진 시간에 알아서 실행하는 자명종 cron — 이 둘의 조합이 자동화의 알파이자 오메가입니다.
1. 학습 목표
이 챕터를 끝내면 다음을 할 수 있습니다:
- 명령어 여러 개를 파일에 모아 한 번에 실행하는 셸 스크립트를 만든다
- 첫 줄의
#!/bin/bash(시뱅)가 무슨 뜻인지 설명한다 $(명령)과 변수로 "날마다 다른 파일명"처럼 변하는 출력을 만든다- cron 시간 표현 다섯 칸(분 시 일 월 요일)을 읽고, 예약을 등록·확인·해제한다
- cron이 안 돌 때 점검하는 두 가지(절대경로, 실행 권한)를 안다
2. 배경 지식 — 오늘의 도구와 개념
오늘의 도구 한눈에 보기
| 구분 | 내용 |
|---|---|
| 언어·환경 | bash 셸 스크립트 + nano — 우분투 터미널 |
| 오늘의 명령어 | chmod +x(실행 권한), ./스크립트.sh(실행), $(명령)(출력 끼워 넣기), crontab -e / crontab -l(예약표 편집·보기), date +%Y-%m-%d(날짜 형식) |
| 필요한 개념 | 시뱅(shebang), 실행 권한, 변수, cron의 다섯 칸 시간 표현, cron 환경과 터미널 환경의 차이 |
2-1. 셸 스크립트의 구조
셸 스크립트는 그냥 "명령어를 위에서 아래로 적은 텍스트 파일"입니다. 다만 두 가지 규칙이 있습니다.
- 첫 줄 — 시뱅(shebang):
#!/bin/bash.#!뒤의 경로는 "이 파일을 어떤 프로그램으로 실행해라"는 표식입니다./bin/bash는 우리가 쓰는 그 셸이죠. 이 줄이 있어야 시스템이 "아, 이건 bash 대본이구나" 하고 알아듣습니다. - 실행 권한: Step 23에서 배운 대로, 파일이 프로그램으로 취급받으려면 x 권한이 필요합니다.
chmod +x를 잊으면 "Permission denied"를 만납니다.
스크립트 안에서는 터미널에서 치던 명령이 전부 그대로 쓰입니다. $(명령)은 "이 명령의 출력을 이 자리에 끼워 넣어라"는 뜻이고, #으로 시작하는 줄(첫 줄 시뱅 제외)은 실행되지 않는 메모, 즉 주석(comment)입니다.
2-2. cron — 리눅스의 자명종
cron은 백그라운드에 상주하는 데몬(Step 26)으로, 1분마다 깨어나 "지금 실행해야 할 예약이 있나?"를 확인하고 실행합니다. 예약표는 사용자마다 한 부씩 있고, crontab -e로 편집합니다(cron table의 약자).
예약표 한 줄의 형식:
* * * * * 실행할_명령
분 시 일 월 요일
다섯 칸이 "언제"입니다. *는 "매번"을 뜻합니다:
* * * * *→ 매 분0 * * * *→ 매시 정각 (분이 0일 때)0 3 * * *→ 매일 새벽 3시*/10 * * * *→ 10분마다
실측 환경(WSL 우분투 24.04)에서도 cron 데몬은 /usr/sbin/cron으로 상주하고 있었습니다 (2026-09-09, ps aux | grep cron으로 확인). 조용히 1분마다 깨어나는 자명종이 실제로 살아 있는 것입니다.
2-3. cron의 함정 두 가지
cron은 우리가 터미널에서 명령을 치는 환경과 다릅니다. 사람이 로그인해서 쓰는 셸에는 여러 환경 설정이 로드되지만, cron은 그런 것 없이 달랑 명령만 실행합니다. 그래서 초보가 반드시 한 번씩 겪는 실수 둘:
- 상대경로 사용:
cron.log처럼 경로 없이 쓰면 "어디에?"의 기준이 달라 파일이 엉뚱한 곳에 생기거나 안 생깁니다. → 경로는 전부/home/계정/...처럼 절대경로로. - 실행 권한 누락:
chmod +x를 안 하면 cron도 못 돌립니다. → 등록 전에 손으로 직접 실행해 보고 확인.
이 두 가지만 기억해도 cron 문제의 절반은 예방됩니다.
3. 따라 하기
3-1. 첫 스크립트 작성
nano hello.sh
nano 안에 다음 세 줄을 쓰고 저장(Ctrl+O, Enter) 후 종료(Ctrl+X)합니다:
#!/bin/bash
# 내 첫 스크립트 — 현재 시각을 인사와 함께 출력
echo "안녕하세요! 지금은 $(date) 입니다."
chmod +x hello.sh
./hello.sh
안녕하세요! 지금은 Wed Sep 9 11:29:10 KST 2026 입니다.
(시각 부분은 2026-09-09 실측 — date의 출력 형식이 실제로 이렇게 나옴을 확인했습니다. 실행 시각은 여러분 것으로 다릅니다.)
읽는 법: $(date) 자리에 date 명령의 출력이 끼워져 나왔습니다. Step 23의 실행 권한, Step 19의 명령 결과 활용이 합쳐진 순간입니다. ./는 "지금 폴더에 있는 이 파일을 실행해"라는 뜻입니다.
왜: 이 세 줄짜리 파일이 "프로그램"입니다. 여러분은 방금 리눅스에서 프로그램을 하나 만들어 실행했습니다.
3-2. 화면 말고 파일에 쌓기
./hello.sh >> hello.log
./hello.sh >> hello.log
cat hello.log
안녕하세요! 지금은 Wed Sep 9 11:31:20 KST 2026 입니다.
안녕하세요! 지금은 Wed Sep 9 11:31:23 KST 2026 입니다.
(출력 예시 — 시각 형식은 실측과 동일합니다.)
읽는 법: Step 19에서 배운 >>(추가 리다이렉션)입니다. 화면 대신 파일 끝에 계속 쌓입니다. 시각이 다른 두 줄이 보이죠.
예측해 보기:
>>가 아니라>를 썼다면 cat 결과가 어떻게 달라질까요? (정답:>는 덮어쓰기라 마지막 한 줄만 남습니다. 자동화 로그는 "쌓는" 것이 목적이므로>>를 씁니다.)
3-3. cron에 예약하기
crontab -e
처음 실행하면 어떤 에디터를 쓸지 물을 수 있습니다. nano(보통 1번)를 고르세요. 주석이 잔뜩 달린 파일이 열리면, 맨 아래에 다음 한 줄을 추가하고 저장·종료합니다 (lee를 자기 계정명으로 바꾸세요):
* * * * * /home/lee/hello.sh >> /home/lee/cron.log 2>&1
crontab: installing new crontab
(출력 예시 — 저장이 성공하면 뜨는 실제 메시지입니다.)
읽는 법: 예약표가 등록됐습니다. 맨 뒤의 2>&1은 "오류 출력(2번 스트림)도 일반 출력(1번)이 가는 곳으로 본낸다"는 뜻 — Step 19의 스트림 지식이 여기서 다시 쓰입니다. 오류 메시지까지 로그에 남겨야 "왜 안 돌았지?"를 나중에 추적할 수 있습니다.
참고로, 예약을 아직 한 번도 등록한 적 없는 계정에서 crontab -l(예약표 보기)을 치면 이렇게 나옵니다:
no crontab for root
(2026-09-09 실측 — 실측 환경은 관리자 계정이라 root로 표시됐고, 여러분은 계정명으로 나옵니다. 등록 후에는 여러분이 넣은 줄이 그대로 보입니다.)
왜: 이제부터 매 분, 여러분이 아무것도 안 해도 hello.sh가 실행되고 결과가 cron.log에 쌓입니다.
3-4. 자동 실행 확인과 해제 (반드시!)
2~3분 기다린 뒤:
cat /home/lee/cron.log
안녕하세요! 지금은 Wed Sep 9 11:35:00 KST 2026 입니다.
안녕하세요! 지금은 Wed Sep 9 11:36:00 KST 2026 입니다.
(출력 예시 — 형식은 실제 동작과 동일합니다.)
읽는 법: 손가락 하나 까딱하지 않는 사이, 정각에 맞춰 줄이 쌓였습니다. 이것이 자동화입니다.
이제 반드시 해제합니다:
crontab -e
아까 추가한 줄을 지우고 저장·종료한 뒤 확인:
crontab -l
읽는 법: -l은 예약표 "보기"입니다. 아까 줄이 사라졌으면 완료입니다.
왜: 매 분 도는 예약을 방치하면 로그가 계속 불어납니다. 자동화의 철칙: 켰으면 끌 줄도 알아야 합니다. Step 25의 계정 정리와 같은 철학입니다.
3-5. 스크립트에 변수 넣기
스크립트가 진짜 프로그램처럼 보이기 시작하는 순간입니다. Step 7의 파워쉘 변수와 같은 개념이 bash에도 있습니다:
nano backup-note.sh
#!/bin/bash
# 오늘 날짜가 들어간 메모 파일을 만드는 스크립트
TODAY=$(date +%Y-%m-%d)
echo "오늘(${TODAY})의 할 일:" > "/home/lee/memo-${TODAY}.txt"
echo "1. 리눅스 복습" >> "/home/lee/memo-${TODAY}.txt"
echo "만든 파일: memo-${TODAY}.txt"
chmod +x backup-note.sh
./backup-note.sh
cat memo-*.txt
만든 파일: memo-2026-09-09.txt
오늘(2026-09-09)의 할 일:
1. 리눅스 복습
(날짜 부분 2026-09-09는 date +%Y-%m-%d 실측 결과 그대로입니다. 실행하는 날짜로 바뀝니다.)
읽는 법: TODAY=$(date +%Y-%m-%d)는 "date 명령의 출력을 TODAY라는 상자에 담아라"입니다. 이후 $TODAY라고 쓰면 그 자리에 내용물이 들어갑니다. +%Y-%m-%d는 date의 출력 형식 지정(연-월-일)으로, 실측에서 2026-09-09가 나옴을 확인했습니다. 파일 이름이 날짜에 따라 매번 바뀌는 것 — 이것이 변수(variable)의 힘입니다.
왜: "매일 다른 이름의 백업 파일" 같은 자동화는 전부 이 패턴입니다. 변수는 스크립트를 "녹음 재생"에서 "생각하는 프로그램"으로 올려 놓는 첫걸음입니다.
4. 미션과 연습문제
미션 — 나만의 시계를 cron에 태우기
clock.sh를 만들어 "현재 시각 + 오늘 날짜"를 출력하게 하세요 (힌트:$(date)와$(date +%Y-%m-%d))chmod +x후 손으로 직접 실행해 출력을 확인하세요 — cron 등록 전 손 실행 확인이 규칙입니다- cron에
*/2 * * * *(짝수 분마다)로 등록해 출력이/home/계정/clock.log에 쌓이는지 5분간 관찰하세요 (끝에2>&1필수) - 쌓인 로그를
cat으로 확인한 뒤, 반드시 예약을 해제하고crontab -l로 비었는지 확인하세요 - 오늘 실험의 전 과정(작성 → 손 실행 → 등록 → 관찰 → 해제)을
cron-report.txt에 다섯 줄로 기록하세요
연습문제
문제 1. 다음 cron 표현이 언제 실행인지 풀어 쓰세요 — 30 2 * * *, 0 9 * * 1, */5 * * * *.
문제 2. 스크립트 첫 줄의 #!/bin/bash는 왜 필요하며, 이 줄이 없으면 무엇이 달라지나요?
문제 3. 손으로 ./hello.sh를 실행하면 되는데 cron으로만 하면 아무 일도 안 일어났습니다. 가장 먼저 점검할 두 가지는 무엇인가요?
문제 4. 예약줄 끝의 >> /home/lee/cron.log 2>&1에서 2>&1은 왜 붙이나요? 없으면 어떤 후회를 하게 되나요?
5. 모범 답안과 완료 기준
미션 모범 답안
nano clock.sh
#!/bin/bash
# clock.sh — 현재 시각과 날짜를 출력하는 시계
echo "지금: $(date) / 오늘: $(date +%Y-%m-%d)"
chmod +x clock.sh
./clock.sh # 손으로 먼저 확인 — 이게 규칙
crontab -e # 맨 아래에 아래 줄 추가
*/2 * * * * /home/lee/clock.sh >> /home/lee/clock.log 2>&1
# 5분 후
cat /home/lee/clock.log
crontab -e # 줄 삭제 후 저장
crontab -l # 비었는지 최종 확인
검증하는 법: ① 손 실행에서 시각·날짜가 둘 다 나왔는가. ② 로그에 2분 간격(시각의 분이 짝수로 두 칸씩 뛰는 것)으로 줄이 쌓였는가 — 이 간격이 cron 표현식을 제대로 썼다는 증거입니다. ③ 해제 후 crontab -l에 방금 줄이 없는가. ④ 5번의 보고서에 "해제까지 했다"가 포함됐는가 — 등록보다 해제를 먼저 확인하는 사람이 진짜 운영자입니다.
연습문제 해답
문제 1 해답. 30 2 * * *는 매일 새벽 2시 30분. 0 9 * * 1은 매주 월요일(요일 1) 오전 9시. */5 * * * *는 5분마다.
문제 2 해답. 시뱅은 "이 파일을 어떤 프로그램으로 실행해라"는 표식입니다. /bin/bash라고 적혀 있어야 시스템이 이 파일을 bash 대본으로 해석합니다. 이 줄이 없으면 실행하는 셸의 기분에 따라 다른 셸이 해석할 수 있어서, bash 전용 문법이 깨질 수 있습니다.
문제 3 해답. ① 경로 — 예약줄과 스크립트 안의 경로가 전부 절대경로인가(cron은 작업 폴더 기준이 다릅니다). ② 실행 권한 — ls -l로 x 권한이 있는가. 진단 순서는 "손으로 되는가 → cron으로 되는가"로 좁혀 가는 것입니다.
문제 4 해답. 2>&1은 오류 출력까지 로그 파일로 보내는 장치입니다. 이것이 없으면 예약 실행이 실패했을 때 오류 메시지가 어디에도 남지 않아 "왜 안 됐는지"를 추적할 방법이 사라집니다. 자동화에서 로그는 장식이 아니라 유일한 목격자입니다.
완료 기준 체크리스트
- [ ] 시뱅(
#!/bin/bash)의 의미를 설명할 수 있다 - [ ]
chmod +x후./스크립트.sh로 스크립트를 실행할 수 있다 - [ ]
$(명령)과 변수로 변하는 출력을 만들 수 있다 - [ ] cron 시간 표현 다섯 칸(분 시 일 월 요일)을 읽을 수 있다
- [ ] cron에 등록하고
crontab -l로 확인하고 해제할 수 있다 - [ ] cron이 안 돌 때 점검할 두 가지(절대경로·실행 권한)를 말할 수 있다
- [ ] 미션: clock.sh 자동 실행과 해제, cron-report.txt를 완성했다
6. 흔한 실수와 해결
벽 1. "분명 등록했는데 cron.log가 안 생겨요"
증상: 몇 분을 기다려도 로그 파일 자체가 없습니다.
원인: 가장 흔한 원인은 경로입니다. 예약줄에서 cron.log처럼 상대경로를 쓰면, cron의 작업 폴더 기준이 달라 다른 곳에 생기거나 쓰기에 실패합니다. 두 번째 원인은 스크립트의 실행 권한 누락입니다.
해결: 경로를 전부 /home/계정/... 절대경로로 고치고, ls -l로 x 권한을 확인하세요. 그리고 손으로 ./hello.sh를 직접 실행해 되는지 먼저 봅니다. "손으로 되는가 → cron으로 되는가" 순서로 좁혀 가세요.
벽 2. "손으로는 되는데 cron으로만 하면 안 돼요"
증상: 직접 실행은 완벽한데 예약 실행만 실패합니다.
원인: 환경 차이입니다. cron은 PATH(명령어를 찾는 폴더 목록)가 최소한이라, 스크립트 안의 일부 명령을 못 찾을 수 있습니다.
해결: 스크립트 안의 명령도 절대경로로 쓰면 확실합니다 (/usr/bin/date처럼 — which date로 위치 확인, 2026-09-09 실측에서 date는 /usr/bin/date였습니다). 그리고 예약줄 끝의 2>&1로 오류 메시지를 로그에 남겨 읽으세요. 오류가 말해 줍니다.
벽 3. "crontab -e를 쳤는데 이상한 에디터가 열려요"
증상: nano가 아니라 vim(또는 다른 에디터)이 열립니다.
원인: 시스템 기본 에디터 설정에 따릅니다. Step 22를 끝낸 여러분에게 vim은 더 이상 공포가 아닙니다.
해결: vim이 열리면 :q!로 나온 뒤 에디터 선택 메뉴에서 nano를 고르세요. 아니면 그냥 vim으로 편집해도 됩니다 — 저장은 :wq.
벽 4. "예약을 지웠는데도 계속 돌아요"
증상: crontab -e에서 줄을 지웠는데 로그가 계속 쌓입니다.
원인: 저장을 안 하고 나갔거나, 다른 예약표(다른 계정, 또는 시스템용 /etc/crontab)에도 등록된 것입니다.
해결: crontab -l로 현재 예약표를 확인하고, 편집 후 "installing new crontab" 메시지를 봤는지 체크하세요. 로그의 최신 줄 시각을 보고 정말 "지금도" 쌓이는지 확인하는 것도 방법입니다 — 마지막 줄 이후로 안 쌓이면 이미 멈춘 것입니다.
벽 5. "스크립트를 실행했더니 Permission denied가 나와요"
증상: ./hello.sh를 쳤더니 bash: ./hello.sh: Permission denied.
원인: 실행 권한이 없습니다. 만들기만 하고 chmod +x를 잊은 것입니다.
해결: chmod +x hello.sh 후 다시 실행하세요. 이 오류는 앞으로 수백 번 더 만날, 리눅스가 권한을 지키는 방식 그 자체입니다.
7. 정리
오늘의 개념
| 개념 | 한 줄 설명 |
|---|---|
| 셸 스크립트 | 명령어를 위에서 아래로 적은 텍스트 파일 — 그게 프로그램 |
시뱅(#!/bin/bash) |
"이 파일을 bash로 실행해라"는 첫 줄 표식 |
$(명령) |
그 명령의 출력을 그 자리에 끼워 넣기 |
| cron | 1분마다 깨어나 예약을 실행하는 자명종 데몬 |
| crontab | 사용자별 예약표 — 분 시 일 월 요일 명령 |
| cron의 2대 함정 | 상대경로 · 실행 권한 누락 |
오늘의 명령어
| 명령 | 하는 일 |
|---|---|
chmod +x 파일 |
실행 권한 부여 |
./스크립트.sh |
지금 폴더의 스크립트 실행 |
>> / 2>&1 |
출력을 파일에 추가 / 오류도 같은 곳으로 |
date +%Y-%m-%d |
날짜를 연-월-일 형식으로 |
crontab -e |
예약표 편집 |
crontab -l |
예약표 보기 (해제 확인용) |
명령어보다 중요한 감각
오늘로 여러분은 "손으로 치는 사람"에서 "시켜 놓는 사람"으로 올라섰습니다. 자동화란 거창한 기술이 아니라, 작은 것들이 만나 생기는 화학 반응입니다 — 세 줄짜리 스크립트가 cron과 만나 "내가 자는 동안에도 일하는 일꾼"이 됐습니다. 그리고 켜고, 확인하고, 끄는 전체 순환까지 돌린 것이 오늘의 완성입니다.
두 가지를 더 기억해 두세요. 첫째, 사용자별 예약표 말고 시스템용 /etc/cron.daily 같은 폴더도 있습니다 — 실측 환경에는 logrotate, man-db, dpkg 같은 시스템 집안일 스크립트가 들어 있었습니다 (2026-09-09 확인). 우분투가 조용히 자기 관리를 하는 데는 cron의 공이 큽니다. 노트북처럼 항상 켜 있지 않은 컴퓨터를 위해서는 "밀린 일을 몰아서 해주는" anacron이라는 보완재도 있습니다. 둘째, cron은 공격자의 은신처이기도 합니다 — 침투한 공격자가 "재부팅해도 살아나는" 장치로 crontab에 악성 명령을 심는 수법(지속성, Step 15의 영속성 개념의 리눅스 판)이 대표적입니다. 그래서 침해 조사 체크리스트에는 "모든 사용자의 crontab 확인"이 반드시 들어갑니다. 오늘 배운 crontab -l이 그 조사 도구입니다. 도구는 같고, 누가 어떻게 쓰느냐가 다릅니다.
앞으로 반복되는 일을 만날 때마다 오늘의 질문을 떠올리세요. "이걸 스크립트로 만들 수 있을까? cron으로 돌릴 수 있을까?" 대답은 거의 언제나 "그렇다"입니다.
전부 체크되면 Step 28 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.