Linux Shell Study

Linux Shell Study입니다. 파일/프로세스/네트워크 명령, 리다이렉션, 권한, cron, 조건 실행, vim, 쉘 스크립트, sed/awk, 장애 대응과 백업 자동화를 정리했습니다.

Linux Shell Study

Linux shell은 파일, 프로세스, 네트워크, 로그, 백업, 자동화를 다루는 운영자의 기본 도구다. 명령어 이름을 나열하는 방식이 아니라, 실제 서버에서 자주 쓰는 순서대로 정리한 쉘 학습 노트다. 모든 예제는 먼저 개인 실습 VM이나 테스트 디렉터리에서 실행한다.

실습 디렉터리 준비

mkdir -p ~/shell-study/{data,logs,backup,tmp}
cd ~/shell-study

printf 'alpha\nbeta\ngamma\n' > data/words.txt
printf 'INFO start\nERROR disk\nINFO end\n' > logs/app.log

파일과 디렉터리 기본 명령

pwd, cd, ls는 현재 위치를 확인하고 이동하는 기본 명령이다. 장애 대응 중에는 내가 어느 디렉터리에서 어떤 파일을 대상으로 작업하는지 먼저 확인해야 한다.

pwd
cd ~
cd ~/shell-study
ls
ls -alh
ls -alhtr
ls -al | awk '{print $9}'
ls 옵션:
-a 숨김 파일 포함
-l 상세 정보 출력
-h 사람이 읽기 쉬운 용량 단위
-t 수정 시간 기준 정렬
-r 역순 정렬

디스크와 마운트

df -h
df -T
df -i
mount | column -t
findmnt

원격 파일시스템을 마운트할 때는 타입과 옵션이 환경마다 다르다. 예를 들어 NFS라면 아래처럼 별도 디렉터리를 만든 뒤 마운트하고, 끝나면 umount로 해제한다.

sudo mkdir -p /mnt/share
sudo mount -t nfs 10.0.101.3:/share /mnt/share
findmnt /mnt/share
sudo umount /mnt/share

파일 생성과 내용 확인

stat data/words.txt
touch tmp/empty.txt
cat data/words.txt
head -n 2 data/words.txt
tail -n 2 data/words.txt
tail -f logs/app.log

tail -F는 로그 파일이 rotate되어 새 파일로 바뀌는 상황에서 유용하다.

tail -F /var/log/syslog 2>/dev/null || tail -F /var/log/messages

복사, 이동, 삭제, 링크

cp -a data/words.txt backup/words.txt.$(date +%Y%m%d)
mv tmp/empty.txt tmp/empty-renamed.txt
rm tmp/empty-renamed.txt

ln data/words.txt hardlink-words
ln -s data/words.txt symlink-words
ls -li data/words.txt hardlink-words symlink-words

rm -rf는 강력한 삭제 명령이다. 운영 서버에서는 변수 확장 결과가 비어 있는지, 대상 경로가 맞는지 확인하지 않고 실행하면 사고로 이어진다.

파일 병합, 블록 복사, 압축

printf '1\n2\n3\n' > data/a.txt
printf 'A\nB\nC\n' > data/b.txt
paste data/a.txt data/b.txt

dd if=/dev/zero of=tmp/10m.bin bs=1M count=10 status=progress
ls -lh tmp/10m.bin
tar -czvf backup/shell-study.tar.gz data logs
tar -tzvf backup/shell-study.tar.gz
mkdir -p tmp/restore
tar -xzvf backup/shell-study.tar.gz -C tmp/restore

프로세스 확인과 종료

ps -ef
ps aux
ps axfww
pstree -p
top

top에서는 1로 CPU core별 표시, m으로 memory 표시 전환, k로 signal 전송을 사용할 수 있다. 프로세스 종료는 가능한 TERM부터 시도하고, KILL은 마지막 수단으로 둔다.

kill -TERM 12345
kill -15 12345
kill -KILL 12345
kill -9 12345

터미널 종료 후에도 작업을 계속 돌려야 하면 nohup 또는 systemd service를 검토한다.

nohup ./long-running-job.sh > logs/job.log 2>&1 &

네트워크 확인

ip addr show
ip route show
ip route get 8.8.8.8
ss -nltpu
netstat -nltpu 2>/dev/null || true
cat /etc/services | head

ifconfig, route, netstat는 오래된 환경에서 아직 보이지만, 신규 스크립트는 ip, ss 중심으로 작성하는 편이 좋다.

ping -c 4 8.8.8.8
wget -O /tmp/example.html https://example.com/
curl -Lks -o /dev/null -w "%{http_code}\n" https://example.com/

방화벽 확인

sudo iptables -nL 2>/dev/null || true
sudo systemctl status iptables --no-pager 2>/dev/null || true
sudo ufw status 2>/dev/null || true
sudo firewall-cmd --list-all 2>/dev/null || true

검색과 PATH

find . -name 'words.txt'
find . -type f -name '*.log'
find . -type d -empty
which bash
command -v curl
echo "${PATH}"

grep과 로그 검색

grep "ERROR" logs/app.log
grep -n "INFO" logs/app.log
grep -R "alpha" .
grep -vi "debug" logs/app.log
grep -c "ERROR" logs/app.log

시스템 로그 경로는 배포판마다 다르다.

ls -l /var/log/messages /var/log/syslog 2>/dev/null || true

리다이렉션과 표준 스트림

stdin은 표준 입력, stdout은 표준 출력, stderr는 표준 오류다. 리다이렉션은 이 흐름을 파일이나 다른 명령으로 바꾼다.

echo "a" > tmp/out.txt
echo "b" > tmp/out.txt
echo "zzz" >> tmp/out.txt
cat tmp/out.txt

ls data missing 1>tmp/ok.log 2>tmp/ng.log
cat tmp/ok.log
cat tmp/ng.log

tee는 화면에도 출력하고 파일에도 저장할 때 쓴다.

df -h | tee logs/df.log
df -h | tee -a logs/df.log

권한과 소유권

ls -l data/words.txt
chmod 644 data/words.txt
chmod 755 data
sudo chown "$USER":"$USER" data/words.txt
r=4, w=2, x=1
644 = owner read/write, group read, other read
755 = owner read/write/execute, group execute/read, other execute/read

사용자, 시간, 숫자열

who
w
history | tail
date
date -d '-1 day'
date -d '1 day ago'
date '+%Y-%m-%d %H:%M:%S'
date '+%Y%m%d'
seq 1 10
seq -w 1 10
seq -f '%03g' 1 10

watch와 crontab

watch -n 2 "df -h"
watch -n 5 "ls -alh logs"
crontab -l
crontab -e
분 시 일 월 요일 명령
0 4 * * * /opt/backup/web_backup.sh >/dev/null 2>&1

crontab -r은 전체 스케줄 삭제라서 운영 서버에서는 특히 주의한다.

명령어 연속 실행

df -h | grep '/dev'
pwd; ls -la
test 1 = 1 && echo "조건이 참입니다"
test 2 = 1 || echo "조건이 거짓입니다"
  • |: 왼쪽 명령의 stdout을 오른쪽 명령의 stdin으로 넘긴다.
  • ;: 앞 명령 종료 후 다음 명령을 실행한다.
  • &&: 앞 명령이 성공하면 뒤 명령을 실행한다.
  • ||: 앞 명령이 실패하면 뒤 명령을 실행한다.

연습 문제

현재 위치에서 tmp 디렉터리를 만들고, 파일을 만들고, 내용을 넣고, 출력한다.

mkdir -p tmp
touch tmp/tmp.txt
echo "tmp" > tmp/tmp.txt
cat tmp/tmp.txt

원격 서버의 프로세스에서 nfs 관련 프로세스를 확인한다. 실제 서버명과 계정은 환경에 맞게 바꾼다.

ssh admin@cent3.example "ps -ef | grep nfs"
ssh admin@cent3.example "pgrep -l nfs"
ssh admin@cent3.example "pgrep -c nfs"

/var/log/messages가 있으면 마지막 15줄을 출력한다.

[ -e /var/log/messages ] && tail -n 15 /var/log/messages

출력을 보기 좋게 정렬한다.

mount | column -t
ss -nltpu | column -t

nano와 vim

nano는 간단한 터미널 편집기다.

Ctrl+G 도움말
Ctrl+O 저장
Ctrl+W 찾기
Ctrl+K 잘라내기
Ctrl+U 붙여넣기
Ctrl+X 종료

vim은 모드 기반 편집기다. 서버에서 설정 파일을 수정할 때 자주 쓰인다.

일반 모드: h j k l 이동, dd 삭제, yy 복사, p 붙여넣기
입력 모드: i, a, o
비주얼 모드: v, V, Ctrl+v
ex 모드: :w 저장, :q 종료, :wq 저장 후 종료, :q! 강제 종료
검색: /검색어, n 다음 결과, N 이전 결과
줄 번호: :set number

vim 안에서 쉘 명령을 실행할 수도 있다.

:!date
:!df -h

쉘 스크립트 기본 구조

#!/usr/bin/env bash
set -euo pipefail

echo "script start"

set -x는 디버깅에는 좋지만 변수 값이 로그에 그대로 찍힐 수 있다. 민감값을 다루는 스크립트에서는 켜지 않는 편이 안전하다.

if, case, 반복문

usage=75

if [ "$usage" -gt 80 ]; then
 echo "disk warning"
elif [ "$usage" -gt 60 ]; then
 echo "disk watch"
else
 echo "disk ok"
fi
service="nginx"

case "$service" in
 nginx)
 echo "web server"
 ;;
 mariadb)
 echo "database"
 ;;
 *)
 echo "unknown"
 ;;
esac
for name in web db storage; do
 echo "server=$name"
done

count=1
while [ "$count" -le 3 ]; do
 echo "$count"
 count=$((count + 1))
done

조건식 정리

숫자 비교:
-eq 같다
-ne 다르다
-gt 크다
-ge 크거나 같다
-lt 작다
-le 작거나 같다

문자열:
= 같다
!= 다르다
-n 문자열 길이가 0보다 크다
-z 문자열 길이가 0이다

파일:
-e 존재한다
-f 일반 파일이다
-d 디렉터리다
-r 읽을 수 있다
-w 쓸 수 있다
-s 존재하고 비어 있지 않다

함수와 배열

log_info() {
 printf '[INFO] %s\n' "$*"
}

servers=(web db storage)

for server in "${servers[@]}"; do
 log_info "check $server"
done

파일을 읽는 while 루프

cat > server_list <<'EOF'
web
db
storage
EOF

while read -r server; do
 echo "server=$server"
done < server_list

대화식 입력과 dialog

read -r -p "service name> " service
echo "selected=$service"

dialog는 터미널 메뉴 UI를 만들 때 쓰는 도구다. 서버에 설치되어 있지 않을 수 있으므로 스크립트에서 사용 전 확인한다.

command -v dialog
dialog --menu "Select job" 15 50 3 \
 backup "Run backup" \
 status "Show status" \
 exit "Exit"

cut, sed, awk

cut -d: -f1 /etc/passwd | head
awk -F: '{print $1}' /etc/passwd | head
uptime | awk -F'load average:' '{print $2}'

sed는 stream editor다. 치환, 출력, 삭제를 빠르게 처리할 수 있다.

printf '이것은 테스트 문장입니다.\n' > data/sed.txt
sed 's/테스트/test/' data/sed.txt
sed -n '/test/p' data/sed.txt
sed -e 's/테스트/test/; s/문장/문자열/' data/sed.txt

여러 sed 규칙을 파일로 분리할 수도 있다.

cat > data/two.sed <<'EOF'
s/테스트/sed test/
s/문장/문자열/
EOF

sed -f data/two.sed data/sed.txt

정규표현식 예시

ls -1 data/*
find . -type f -iname 't*'
find . -iname 'txt?'
ls -al tmp/[0-9]* 2>/dev/null || true
sed -n '/^INFO/p' logs/app.log
sed -n '/ERROR$/p' logs/app.log

awk로 디스크 사용률 추출

df -h | awk '{print $1, $2, $3, $4, $5, $6}' | column -t
df -h | awk '{gsub("%",""); print $5, $6}' | column -t
df -h | awk '{gsub("%",""); USE=$5; MNT=$6; if (USE > 80) print MNT, USE "%"}' | grep -v 'Mounted'

장애 대응: 디스크, 메모리, 부하

웹 서버에서 자주 보는 장애는 디스크 full, 메모리 부족, load average 급등, 로그 폭증, 백업 경로 오류다. 원인을 좁힐 때는 전체 사용량에서 시작해 디렉터리별 사용량, 프로세스, 로그 순서로 내려간다.

df -h
du -h --max-depth=1 /var 2>/dev/null | sort -h
du -sh /var/log/* 2>/dev/null | sort -h | tail
free -m
uptime
ps aux --sort=-%mem | head -10
ps aux --sort=-%cpu | head -10
watch -n 5 'df -h'
watch -n 5 'du -sh /var/log/* 2>/dev/null | sort -h | tail'

crontab 감시 예시

# 로그 디렉터리 감시
0 * * * * /opt/monitor/log_min.sh >/dev/null 2>&1

# 디스크 파티션 감시
30 * * * * /opt/monitor/part_mon.sh >/dev/null 2>&1

# 백업 후 마운트 상태 감시
0 5 * * * /opt/monitor/mnt_mon.sh >/dev/null 2>&1

백업 기준

백업은 무엇을, 언제, 어디에, 어떻게 복구할지까지 같이 정해야 한다.

  • 웹: 웹 소스, 업로드 파일, Nginx/Apache 설정, 애플리케이션 설정
  • DB: dump 또는 전용 백업 도구, binlog/backup retention 정책
  • 스토리지: 설정 파일과 데이터 복제/스냅샷 정책
  • 시간: 트래픽이 가장 낮은 새벽 시간대
  • 위치: 로컬 디스크가 아니라 별도 백업 스토리지
hostname="$(hostname)"
backup_dir="/mnt/BACKUP/${hostname}"
sudo mkdir -p "$backup_dir"

tar -czpf "${backup_dir}/web-$(date +%Y%m%d).tar.gz" \
 /etc/nginx \
 /var/www 2>/tmp/web-backup.err

-p는 permission 보존, -P는 절대경로 보존이다. 절대경로 보존은 복구 시 원치 않는 위치에 덮어쓸 수 있으므로 신중히 사용한다.

백업 스크립트 품질 기준

#!/usr/bin/env bash
set -euo pipefail

backup_root="/mnt/BACKUP/$(hostname)"
today="$(date +%Y%m%d)"

if ! findmnt /mnt/BACKUP >/dev/null; then
 echo "backup storage is not mounted" >&2
 exit 1
fi

mkdir -p "$backup_root"
tar -czpf "${backup_root}/etc-${today}.tar.gz" /etc

장애 사례에서 자주 나오는 문제는 백업 스토리지 마운트 실패 후 로컬 디스크에 백업 파일이 쌓이는 상황이다. 백업 스크립트는 쓰기 전에 반드시 마운트 상태를 확인해야 한다.

find와 보존 기간 관리

find ./backup -type f -name '*.tar.gz' -mtime +30 -print
find ./backup -type f -name '*.tar.gz' -mtime +30 -delete

삭제 명령을 자동화할 때는 처음부터 -delete를 붙이지 말고 -print로 대상 목록을 검토한다.

운영 팁

mount | column -t
ls -al | column -t
df -h | column -t
ps aux | sort -nr -k 4 | head -10
timeout 10s curl -I https://example.com/
find . -xdev -type d \
 -exec /bin/echo -n "{} : " \; \
 -exec sh -c 'ls "$1" | wc -l' sh {} \;
while true; do
 date
 ps auxf | awk '{if ($8 == "D") print $0}'
 sleep 1
done
ps -eo pmem,comm |
 grep -i "PROCESS_NAME" |
 awk '{sum += $1} END {print sum "% of memory"}'

쉘은 작은 명령을 조합하는 도구다. 한 줄 명령이 길어질수록 실수 비용이 커지므로, 운영에 반복 적용할 내용은 스크립트로 분리하고 set -euo pipefail, 로그, 사전 검증, 복구 절차를 함께 둔다.

BGM EVER