- Published on
Linux 커맨드라인 완전 정복: 개발자가 반드시 알아야 할 100가지 명령어와 실전 활용
- Authors

- Name
- Youngju Kim
- @fjvbn20031
1. 왜 Linux 커맨드라인인가?
개발자가 GUI 대신 터미널을 써야 하는 이유는 명확합니다. 속도, 자동화, 원격 서버 접근. 클라우드 서버의 90% 이상이 Linux를 사용하고, CI/CD 파이프라인은 모두 CLI 기반입니다. 커맨드라인을 모르면 서버에서 아무것도 할 수 없습니다.
이 글에서 다루는 내용
- 파일 시스템 탐색과 조작 명령어 20가지
- 텍스트 처리의 삼총사: grep, awk, sed 실전 예제
- 프로세스와 서비스 관리
- 네트워크 디버깅과 SSH 마스터하기
- Bash 스크립팅 기초부터 실전까지
- tmux와 Vim 필수 사용법
- 시스템 모니터링과 트러블슈팅
2. 파일 시스템 탐색 (Navigation)
2.1 기본 탐색 명령어
# 현재 디렉토리 확인
pwd
# /home/developer/projects
# 디렉토리 이동
cd /var/log # 절대 경로
cd ../config # 상대 경로
cd ~ # 홈 디렉토리
cd - # 이전 디렉토리
# 디렉토리 내용 확인
ls -la # 숨김 파일 포함 상세 목록
ls -lhS # 파일 크기 순 정렬 (사람이 읽기 쉬운 단위)
ls -lt # 수정 시간 순 정렬
ls -R # 재귀적 목록
2.2 파일 검색
# find: 디렉토리 트리에서 파일 검색
find /var/log -name "*.log" -mtime -7 # 7일 이내 수정된 .log 파일
find . -type f -size +100M # 100MB 이상 파일
find . -name "*.tmp" -exec rm -f {} \; # .tmp 파일 찾아서 삭제
find . -type f -name "*.js" ! -path "*/node_modules/*" # node_modules 제외
# locate: 색인 기반 빠른 검색
sudo updatedb # 데이터베이스 업데이트
locate nginx.conf # 빠른 파일 검색
# which / whereis: 실행 파일 위치
which python3 # /usr/bin/python3
whereis nginx # 바이너리, 소스, 매뉴얼 위치
# tree: 디렉토리 구조 시각화
tree -L 2 -I "node_modules|.git" # 2단계 깊이, 특정 디렉토리 제외
2.3 디렉토리 관리
# 디렉토리 생성
mkdir -p project/src/components # 중간 디렉토리 자동 생성
# 디렉토리 스택 활용
pushd /etc/nginx # 현재 위치 저장 후 이동
# ... 작업 수행 ...
popd # 저장된 위치로 복귀
dirs -v # 스택 확인
3. 파일 조작 (File Manipulation)
3.1 복사, 이동, 삭제
# 복사
cp -r src/ backup/ # 디렉토리 재귀 복사
cp -p important.conf important.conf.bak # 권한/타임스탬프 보존
# 이동/이름 변경
mv old_name.txt new_name.txt # 이름 변경
mv *.log /var/log/archive/ # 여러 파일 이동
# 삭제 (주의!)
rm -rf build/ # 디렉토리 강제 삭제 (매우 주의)
rm -i *.tmp # 삭제 전 확인
# 안전한 대안
trash-put file.txt # 휴지통으로 이동 (trash-cli 패키지)
3.2 파일 권한과 소유권
# 권한 변경
chmod 755 script.sh # rwxr-xr-x
chmod +x deploy.sh # 실행 권한 추가
chmod -R 644 public/ # 재귀적 권한 변경
# 소유권 변경
chown www-data:www-data /var/www # 소유자:그룹 변경
chown -R developer: project/ # 재귀적 소유자 변경
# 권한 숫자 이해
# r(4) + w(2) + x(1) = 7 (소유자)
# r(4) + x(1) = 5 (그룹)
# r(4) + x(1) = 5 (기타)
# 결과: 755
3.3 링크와 아카이브
# 심볼릭 링크 (바로가기)
ln -s /usr/local/bin/python3.11 /usr/local/bin/python
ls -la /usr/local/bin/python # 링크 확인
# 하드 링크
ln original.txt hardlink.txt # 같은 inode 공유
# 아카이브와 압축
tar -czf backup.tar.gz project/ # gzip 압축 아카이브 생성
tar -xzf backup.tar.gz # 압축 해제
tar -xjf archive.tar.bz2 # bzip2 압축 해제
zip -r project.zip project/ -x "*/node_modules/*" # node_modules 제외 zip
unzip project.zip -d /tmp/ # 특정 디렉토리에 해제
4. 텍스트 처리의 삼총사: grep, awk, sed
4.1 grep - 패턴 검색
# 기본 검색
grep "ERROR" /var/log/syslog # ERROR 포함 줄 검색
grep -i "warning" app.log # 대소문자 무시
grep -r "TODO" src/ --include="*.py" # Python 파일에서 재귀 검색
grep -n "function" script.js # 줄 번호 표시
# 정규표현식
grep -E "^[0-9]{4}-[0-9]{2}" access.log # 날짜 패턴으로 시작하는 줄
grep -P "\d+\.\d+\.\d+\.\d+" access.log # IP 주소 패턴 (Perl 정규식)
grep -v "^#" config.ini # 주석 제외
# 실전: 로그에서 에러 컨텍스트 확인
grep -B 3 -A 5 "Exception" app.log # 에러 전후 줄 포함
# 실전: 특정 HTTP 상태 코드 추출
grep -oP 'HTTP/\d\.\d" \K5\d{2}' access.log | sort | uniq -c | sort -rn
4.2 awk - 텍스트 처리 프로그래밍
# 필드 추출
awk '{print $1, $4}' access.log # 1번째, 4번째 필드 출력
awk -F: '{print $1, $3}' /etc/passwd # 구분자 지정
# 조건 필터링
awk '$9 >= 500' access.log # HTTP 5xx 에러만
awk '$3 > 1000 {print $1, $3}' data.txt # 3번째 필드가 1000 초과
# 계산
awk '{sum += $5} END {print "Total:", sum}' sales.txt # 합계
awk '{sum += $1; n++} END {print sum/n}' numbers.txt # 평균
# 실전: 로그에서 IP별 요청 수 카운트
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20
# 실전: CSV 처리
awk -F',' '{
if ($3 > 100) {
total += $3
count++
}
}
END {
printf "Average: %.2f\n", total/count
}' sales.csv
# 실전: 특정 시간대 로그 필터링
awk '/2026-03-23 14:[0-5][0-9]/' application.log
4.3 sed - 스트림 편집기
# 문자열 치환
sed 's/old/new/g' file.txt # 모든 old를 new로
sed -i 's/localhost/0.0.0.0/g' config.yml # 파일 직접 수정 (-i)
sed -i.bak 's/debug/info/g' app.conf # 백업 생성 후 수정
# 줄 삭제
sed '/^$/d' file.txt # 빈 줄 삭제
sed '/^#/d' config.ini # 주석 줄 삭제
sed '1,5d' file.txt # 1-5번째 줄 삭제
# 줄 추가/삽입
sed '3a\New line after line 3' file.txt # 3번째 줄 뒤에 추가
sed '1i\Header Line' file.txt # 1번째 줄 앞에 삽입
# 실전: 설정 파일에서 특정 블록 추출
sed -n '/\[database\]/,/\[/p' config.ini
# 실전: 여러 치환을 한번에
sed -e 's/foo/bar/g' -e 's/baz/qux/g' -e '/^$/d' input.txt
4.4 기타 텍스트 도구
# cut: 필드 잘라내기
cut -d: -f1,3 /etc/passwd # 구분자로 필드 추출
cut -c1-10 file.txt # 문자 위치로 추출
# sort와 uniq
sort -t: -k3 -n /etc/passwd # 3번째 필드 숫자 정렬
sort -u file.txt # 정렬 + 중복 제거
uniq -c sorted.txt # 중복 횟수 카운트
# wc: 카운트
wc -l *.py # 파일별 줄 수
find . -name "*.java" | xargs wc -l | tail -1 # Java 전체 줄 수
# head / tail
head -20 file.txt # 처음 20줄
tail -f /var/log/syslog # 실시간 로그 모니터링
tail -f app.log | grep --line-buffered "ERROR" # 실시간 에러 필터링
# diff: 파일 비교
diff -u old.conf new.conf # 통합 diff 형식
diff -rq dir1/ dir2/ # 디렉토리 비교 (다른 파일만)
# tr: 문자 변환
echo "Hello World" | tr 'A-Z' 'a-z' # 소문자 변환
cat file.txt | tr -d '\r' # Windows 줄바꿈 제거
# xargs: 파이프 결과를 인자로 전달
find . -name "*.log" | xargs gzip # 찾은 파일 압축
cat urls.txt | xargs -P 4 -I {} curl -sO {} # 4개 병렬 다운로드
5. 프로세스 관리
5.1 프로세스 확인
# ps: 프로세스 목록
ps aux # 전체 프로세스
ps aux | grep nginx # 특정 프로세스 검색
ps -ef --forest # 프로세스 트리
# top / htop: 실시간 모니터링
top -o %MEM # 메모리 사용량 순
htop # 대화형 모니터링 (더 직관적)
# pgrep / pidof: PID 찾기
pgrep -f "python app.py" # 이름으로 PID 검색
pidof nginx # 프로세스 PID 확인
5.2 프로세스 제어
# kill: 프로세스 종료
kill -15 1234 # SIGTERM (정상 종료 요청)
kill -9 1234 # SIGKILL (강제 종료)
killall nginx # 이름으로 모든 프로세스 종료
pkill -f "node server" # 패턴 매칭으로 종료
# 백그라운드 실행
nohup python server.py > output.log 2>&1 & # 로그아웃해도 유지
disown %1 # 현재 쉘에서 분리
# jobs: 백그라운드 작업 관리
jobs -l # 현재 쉘의 백그라운드 작업
fg %1 # 포그라운드로 전환
bg %1 # 백그라운드에서 계속 실행
5.3 systemd 서비스 관리
# 서비스 상태 확인/제어
sudo systemctl status nginx # 상태 확인
sudo systemctl start nginx # 시작
sudo systemctl stop nginx # 중지
sudo systemctl restart nginx # 재시작
sudo systemctl reload nginx # 설정 리로드 (무중단)
sudo systemctl enable nginx # 부팅 시 자동 시작
# 서비스 로그 확인
journalctl -u nginx -f # 실시간 로그
journalctl -u nginx --since "1 hour ago" # 최근 1시간 로그
journalctl -u nginx --no-pager -n 100 # 최근 100줄
journalctl -p err -b # 현재 부팅의 에러 로그만
# 서비스 파일 위치 확인
systemctl show nginx -p FragmentPath
6. 네트워크 명령어
6.1 HTTP 요청과 다운로드
# curl: HTTP 요청
curl -s https://api.example.com/data # 기본 GET
curl -X POST -H "Content-Type: application/json" \
-d '{"name":"test"}' https://api.example.com/users
curl -o file.zip https://example.com/file.zip # 파일 다운로드
curl -I https://example.com # 헤더만 확인
curl -w "@curl-format.txt" -o /dev/null -s https://example.com # 응답 시간 측정
# wget: 파일 다운로드
wget -c https://example.com/large-file.iso # 이어받기
wget -r -np -l 2 https://example.com/docs/ # 재귀 다운로드 (2단계)
6.2 네트워크 진단
# 연결 확인
ss -tlnp # 열린 TCP 포트 확인
ss -tlnp | grep :8080 # 특정 포트 사용 프로세스
netstat -tulpn # netstat 대안 (구버전)
# DNS 조회
dig example.com # DNS 조회
dig +short example.com A # 간단한 A 레코드 조회
dig @8.8.8.8 example.com # 특정 DNS 서버로 조회
nslookup example.com # 간단한 DNS 조회
# 경로 추적
traceroute example.com # 네트워크 경로 추적
mtr example.com # 실시간 traceroute + ping
# 방화벽 (iptables / nftables)
sudo iptables -L -n -v # 현재 규칙 확인
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT # HTTP 포트 허용
sudo iptables -A INPUT -s 192.168.1.0/24 -j ACCEPT # 특정 서브넷 허용
# 네트워크 인터페이스
ip addr show # IP 주소 확인
ip route show # 라우팅 테이블
7. SSH 마스터하기
7.1 SSH 기본과 설정
# SSH 키 생성
ssh-keygen -t ed25519 -C "dev@example.com"
# 공개키 복사
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
# SSH 접속
ssh user@192.168.1.100
ssh -p 2222 user@server # 특정 포트
SSH 설정 파일로 접속을 편리하게 관리할 수 있습니다.
# ~/.ssh/config
Host production
HostName 10.0.1.50
User deploy
Port 22
IdentityFile ~/.ssh/prod_key
ForwardAgent yes
Host staging
HostName 10.0.2.50
User deploy
IdentityFile ~/.ssh/staging_key
Host bastion
HostName bastion.example.com
User admin
IdentityFile ~/.ssh/bastion_key
Host internal-*
ProxyJump bastion
User developer
7.2 SSH 터널링
# 로컬 포트 포워딩: 원격 서비스에 로컬로 접근
# 로컬 5432 포트 -> 원격 서버의 DB(5432)에 연결
ssh -L 5432:db-server:5432 bastion-server
# 리버스 포트 포워딩: 로컬 서비스를 원격에서 접근
# 원격의 8080 -> 로컬의 3000
ssh -R 8080:localhost:3000 remote-server
# 동적 포트 포워딩 (SOCKS 프록시)
ssh -D 1080 proxy-server
# 실전: 여러 터널 한번에
ssh -L 5432:db:5432 -L 6379:redis:6379 -L 9200:elastic:9200 bastion
7.3 파일 전송
# scp: 간단한 파일 전송
scp local-file.txt user@server:/remote/path/
scp -r local-dir/ user@server:/remote/path/
scp user@server:/remote/file.txt ./local/
# rsync: 효율적인 동기화 (변경분만 전송)
rsync -avz --progress local/ user@server:/remote/
rsync -avz --delete local/ user@server:/remote/ # 원본에 없는 파일 삭제
rsync -avz --exclude "node_modules" --exclude ".git" \
project/ user@server:/deploy/
8. Bash 스크립팅
8.1 기초 문법
#!/bin/bash
set -euo pipefail # 에러 발생 시 즉시 중단, 미선언 변수 사용 방지
# 변수
APP_NAME="my-app"
VERSION="1.0.0"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# 문자열 조작
echo "App: $APP_NAME, Version: $VERSION"
echo "Length: ${#APP_NAME}" # 문자열 길이
echo "${APP_NAME^^}" # 대문자 변환 (MY-APP)
echo "${APP_NAME/my/your}" # 치환 (your-app)
8.2 조건문과 반복문
#!/bin/bash
# 조건문
if [ -f "/etc/nginx/nginx.conf" ]; then
echo "Nginx config exists"
elif [ -f "/etc/apache2/apache2.conf" ]; then
echo "Apache config exists"
else
echo "No web server config found"
fi
# 파일 테스트 연산자
# -f: 파일 존재
# -d: 디렉토리 존재
# -r: 읽기 권한
# -w: 쓰기 권한
# -x: 실행 권한
# -s: 파일 크기가 0보다 큼
# for 루프
for server in web1 web2 web3; do
echo "Deploying to $server..."
ssh "$server" "cd /app && git pull && systemctl restart app"
done
# 파일 목록 순회
for file in /var/log/*.log; do
echo "Processing: $file"
gzip "$file"
done
# while 루프
while read -r line; do
echo "Processing: $line"
done < input.txt
# 카운터 루프
count=0
while [ $count -lt 10 ]; do
echo "Attempt $count"
((count++))
done
8.3 함수와 에러 처리
#!/bin/bash
set -euo pipefail
# 함수 정의
deploy() {
local environment="$1"
local version="$2"
echo "Deploying version $version to $environment..."
if ! docker pull "myapp:$version" 2>/dev/null; then
echo "Error: Image myapp:$version not found"
return 1
fi
docker stop myapp 2>/dev/null || true
docker run -d --name myapp "myapp:$version"
echo "Deploy complete!"
}
# 에러 핸들링
cleanup() {
echo "Cleaning up temporary files..."
rm -rf /tmp/deploy_*
}
trap cleanup EXIT # 스크립트 종료 시 항상 실행
# getopts로 인자 파싱
usage() {
echo "Usage: deploy.sh -e ENVIRONMENT -v VERSION [-d]"
exit 1
}
DRY_RUN=false
while getopts "e:v:dh" opt; do
case $opt in
e) ENVIRONMENT="$OPTARG" ;;
v) VERSION="$OPTARG" ;;
d) DRY_RUN=true ;;
h) usage ;;
*) usage ;;
esac
done
# 실행
deploy "$ENVIRONMENT" "$VERSION"
8.4 실전 스크립트: 자동 백업
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/backup"
DB_NAME="production"
RETENTION_DAYS=30
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/${DB_NAME}_$TIMESTAMP.sql.gz"
# 로깅 함수
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# 백업 실행
log "Starting backup of $DB_NAME..."
pg_dump "$DB_NAME" | gzip > "$BACKUP_FILE"
# 백업 검증
if [ -s "$BACKUP_FILE" ]; then
SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
log "Backup successful: $BACKUP_FILE ($SIZE)"
else
log "ERROR: Backup file is empty!"
exit 1
fi
# 오래된 백업 정리
log "Removing backups older than $RETENTION_DAYS days..."
find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" -mtime +$RETENTION_DAYS -delete
# 남은 백업 수 확인
REMAINING=$(find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" | wc -l)
log "Done. $REMAINING backups remaining."
9. tmux - 터미널 멀티플렉서
9.1 기본 사용법
# 세션 관리
tmux new -s dev # 새 세션 생성
tmux ls # 세션 목록
tmux attach -t dev # 세션 연결
tmux kill-session -t dev # 세션 종료
9.2 주요 단축키
tmux의 기본 프리픽스 키는 Ctrl+b입니다.
| 동작 | 단축키 |
|---|---|
| 수평 분할 | Ctrl+b % |
| 수직 분할 | Ctrl+b " |
| 패널 이동 | Ctrl+b 방향키 |
| 새 창 | Ctrl+b c |
| 다음 창 | Ctrl+b n |
| 이전 창 | Ctrl+b p |
| 세션 분리 | Ctrl+b d |
| 패널 크기 조절 | Ctrl+b Alt+방향키 |
| 패널 확대/축소 | Ctrl+b z |
9.3 추천 tmux 설정
# ~/.tmux.conf
set -g mouse on # 마우스 지원
set -g history-limit 50000 # 스크롤백 버퍼 확대
set -g base-index 1 # 창 번호 1부터 시작
setw -g pane-base-index 1 # 패널 번호 1부터 시작
# 프리픽스 키 변경 (Ctrl+a)
unbind C-b
set -g prefix C-a
# 직관적 분할 키
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
# Vi 모드
setw -g mode-keys vi
10. Vim 필수 사용법
10.1 모드와 기본 명령어
| 모드 | 진입 | 설명 |
|---|---|---|
| Normal | Esc | 명령어 실행 |
| Insert | i, a, o | 텍스트 입력 |
| Visual | v, V | 텍스트 선택 |
| Command | : | Ex 명령어 |
10.2 생존 필수 명령어
# 이동
h/j/k/l 왼/아래/위/오른
w / b 단어 앞/뒤로
0 / $ 줄 시작/끝
gg / G 파일 처음/끝
:42 42번째 줄로 이동
# 편집
dd 줄 삭제
yy 줄 복사
p 붙여넣기
u 실행 취소
Ctrl+r 다시 실행
. 마지막 명령 반복
# 검색/치환
/pattern 앞으로 검색
?pattern 뒤로 검색
n / N 다음/이전 결과
:%s/old/new/g 전체 치환
:%s/old/new/gc 확인하며 치환
# 저장/종료
:w 저장
:q 종료
:wq 저장 후 종료
:q! 저장 안 하고 종료
10.3 실전 Vim 팁
# 여러 파일 편집
:e filename 파일 열기
:bn / :bp 다음/이전 버퍼
:vs filename 수직 분할 열기
# 매크로
qa 매크로 a 녹화 시작
q 녹화 중지
@a 매크로 a 실행
100@a 100번 반복 실행
# 블록 편집 (여러 줄 주석 등)
Ctrl+v 블록 선택
I 블록 앞에 삽입
Esc 적용
11. 시스템 모니터링
11.1 메모리와 디스크
# 메모리
free -h # 메모리 사용량
cat /proc/meminfo # 상세 메모리 정보
# 디스크
df -h # 디스크 사용량
du -sh /var/log/* # 디렉토리별 크기
du -h --max-depth=1 / | sort -rh | head -10 # 가장 큰 디렉토리
# 디스크 I/O
iostat -x 1 # I/O 통계 (1초 간격)
iotop # 프로세스별 I/O 사용량
11.2 CPU와 시스템 성능
# CPU 정보
lscpu # CPU 정보
nproc # CPU 코어 수
uptime # 로드 평균
# vmstat: 가상 메모리 통계
vmstat 1 10 # 1초 간격 10번
# sar: 시스템 활동 보고서
sar -u 1 5 # CPU 사용률 (1초 간격 5번)
sar -r 1 5 # 메모리 사용률
sar -n DEV 1 5 # 네트워크 트래픽
# lsof: 열린 파일 목록
lsof -i :8080 # 8080 포트 사용 프로세스
lsof -u developer # 특정 사용자의 열린 파일
lsof +D /var/log/ # 특정 디렉토리의 열린 파일
# strace: 시스템 콜 추적 (디버깅용)
strace -f -e trace=network -p 1234 # 네트워크 관련 시스템 콜 추적
12. 패키지 관리
12.1 Debian/Ubuntu (apt)
sudo apt update # 패키지 목록 업데이트
sudo apt upgrade # 설치된 패키지 업그레이드
sudo apt install nginx # 패키지 설치
sudo apt remove nginx # 패키지 제거
sudo apt autoremove # 불필요한 패키지 정리
apt search keyword # 패키지 검색
apt show nginx # 패키지 정보
dpkg -l | grep nginx # 설치된 패키지 확인
12.2 RHEL/CentOS (yum/dnf)
sudo yum update # 업데이트
sudo yum install httpd # 설치
sudo yum remove httpd # 제거
yum search keyword # 검색
rpm -qa | grep httpd # 설치된 패키지 확인
12.3 macOS (brew)
brew update # Homebrew 업데이트
brew install jq # 패키지 설치
brew upgrade # 모든 패키지 업그레이드
brew list # 설치된 패키지 목록
brew cleanup # 캐시 정리
13. Docker CLI 필수 명령어
# 이미지 관리
docker images # 이미지 목록
docker pull nginx:latest # 이미지 다운로드
docker build -t myapp:1.0 . # 이미지 빌드
docker rmi myapp:1.0 # 이미지 삭제
# 컨테이너 관리
docker ps # 실행 중인 컨테이너
docker ps -a # 모든 컨테이너
docker run -d -p 8080:80 --name web nginx # 백그라운드 실행
docker stop web # 컨테이너 중지
docker rm web # 컨테이너 삭제
# 디버깅
docker logs -f web # 실시간 로그
docker exec -it web /bin/bash # 컨테이너 내부 접속
docker inspect web # 상세 정보
# 정리
docker system prune -a # 미사용 리소스 전체 정리
docker volume prune # 미사용 볼륨 정리
14. 파이프와 리다이렉트 심화
# 기본 리다이렉트
command > output.txt # 표준 출력을 파일로 (덮어쓰기)
command >> output.txt # 표준 출력을 파일로 (추가)
command 2> error.log # 표준 에러를 파일로
command > output.txt 2>&1 # 표준 출력 + 에러를 같은 파일로
command &> all.log # 위와 동일 (Bash 단축형)
# 파이프 체이닝 실전
# 가장 많은 메모리를 사용하는 프로세스 10개
ps aux --sort=-%mem | head -11
# 로그에서 에러 빈도 분석
cat app.log | grep "ERROR" | awk '{print $4}' | sort | uniq -c | sort -rn | head
# 실시간 로그에서 특정 패턴 알림
tail -f app.log | grep --line-buffered "CRITICAL" | while read line; do
echo "ALERT: $line" | mail -s "Critical Error" admin@example.com
done
# 프로세스 치환
diff <(ssh server1 cat /etc/config) <(ssh server2 cat /etc/config)
# Here document
cat <<'SCRIPT' > /tmp/setup.sh
#!/bin/bash
echo "Running setup..."
apt update && apt install -y nginx
SCRIPT
15. 10가지 트러블슈팅 시나리오
시나리오 1: 디스크 공간 부족
# 1. 전체 디스크 사용량 확인
df -h
# 2. 큰 디렉토리 찾기
du -h --max-depth=1 / 2>/dev/null | sort -rh | head -10
# 3. 큰 파일 찾기
find / -type f -size +500M 2>/dev/null | head -20
# 4. 삭제되었지만 프로세스가 잡고 있는 파일 확인
lsof +L1
# 5. 로그 파일 정리
sudo journalctl --vacuum-size=500M
find /var/log -name "*.gz" -mtime +30 -delete
시나리오 2: 서비스가 응답하지 않음
# 1. 서비스 상태 확인
sudo systemctl status myapp
# 2. 포트 리스닝 확인
ss -tlnp | grep :8080
# 3. 프로세스 상태 확인
ps aux | grep myapp
# 4. 최근 로그 확인
journalctl -u myapp --since "10 minutes ago" --no-pager
# 5. 리소스 확인 (OOM Killer)
dmesg | grep -i "oom\|killed"
시나리오 3: 높은 CPU 사용률
# 1. CPU 소비 프로세스 확인
top -o %CPU -bn1 | head -15
# 2. 특정 프로세스의 스레드 확인
top -H -p $(pgrep myapp)
# 3. 시스템 콜 확인
strace -c -p $(pgrep myapp) -e trace=all
# 4. 로드 평균 히스토리
sar -u 1 10
시나리오 4: 네트워크 연결 문제
# 1. DNS 확인
dig api.example.com +short
# 2. 포트 연결 테스트
nc -zv api.example.com 443
# 3. 경로 추적
traceroute api.example.com
# 4. SSL 인증서 확인
openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null | openssl x509 -text -noout | grep -A2 "Validity"
시나리오 5: 메모리 누수 조사
# 1. 메모리 사용량 추이 확인
watch -n 5 'free -h'
# 2. 프로세스별 메모리 사용량
ps aux --sort=-%mem | head -10
# 3. 특정 프로세스 메모리 상세
cat /proc/$(pgrep myapp)/status | grep -i "vm\|mem"
# 4. OOM Killer 로그
dmesg -T | grep -i "oom"
시나리오 6: 느린 디스크 I/O
# 1. I/O 대기 확인
iostat -x 1 5
# 2. I/O 많이 사용하는 프로세스
iotop -oP
# 3. 디스크 성능 테스트
dd if=/dev/zero of=testfile bs=1G count=1 oflag=direct
시나리오 7: SSH 접속 실패
# 1. 상세 디버그 모드로 접속 시도
ssh -vvv user@server
# 2. SSH 서비스 상태 확인 (서버에서)
sudo systemctl status sshd
# 3. 방화벽 확인
sudo iptables -L -n | grep 22
# 4. 인증 로그 확인
sudo tail -50 /var/log/auth.log
시나리오 8: 파일 권한 문제
# 1. 파일 권한 확인
ls -la /path/to/file
namei -l /path/to/file # 전체 경로의 권한 확인
# 2. SELinux / AppArmor 확인
getenforce # SELinux 상태
ls -Z /path/to/file # SELinux 컨텍스트
# 3. ACL 확인
getfacl /path/to/file
시나리오 9: 로그 분석으로 공격 탐지
# 1. 비정상 로그인 시도 확인
grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head
# 2. 웹 서버 비정상 접근 확인
awk '$9 == 404' access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head
# 3. 동시 접속 수 확인
ss -tn | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head
시나리오 10: 프로세스가 좀비 상태
# 1. 좀비 프로세스 확인
ps aux | awk '$8 ~ /Z/ {print}'
# 2. 부모 프로세스 찾기
ps -o ppid= -p ZOMBIE_PID
# 3. 부모 프로세스 재시작으로 정리
kill -SIGCHLD PARENT_PID
# 또는 부모 프로세스 재시작
16. 유용한 원라이너 모음
# 현재 디렉토리에서 가장 큰 파일 10개
find . -type f -exec du -h {} + | sort -rh | head -10
# 특정 포트를 사용하는 프로세스 즉시 종료
kill $(lsof -t -i:8080)
# Git 저장소에서 가장 큰 파일 찾기
git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | sed -n 's/^blob //p' | sort -snk2 | tail -10
# 서버 응답 시간 측정 (10회 반복)
for i in $(seq 1 10); do curl -o /dev/null -s -w "%{time_total}\n" https://example.com; done
# JSON 파일 예쁘게 출력
cat data.json | python3 -m json.tool
# 또는
cat data.json | jq .
# 파일 인코딩 변환
iconv -f EUC-KR -t UTF-8 input.txt > output.txt
# 현재 쉘의 모든 환경변수를 정렬해서 보기
env | sort
# 특정 확장자 파일의 총 줄 수
find . -name "*.py" -exec wc -l {} + | tail -1
17. 면접 질문 15선
Q1. hard link와 symbolic link의 차이점은?
Hard link: 동일한 inode를 가리킵니다. 원본이 삭제되어도 접근 가능합니다. 파일 시스템을 넘어갈 수 없고, 디렉토리에 사용할 수 없습니다.
Symbolic link: 별도의 inode로 경로를 가리킵니다. 원본이 삭제되면 깨진 링크가 됩니다. 파일 시스템을 넘어 사용 가능하고, 디렉토리도 가능합니다.
# Hard link: 같은 inode 공유
ln original.txt hard.txt
ls -li original.txt hard.txt # 같은 inode 번호
# Symbolic link: 다른 inode, 경로 참조
ln -s original.txt sym.txt
ls -li original.txt sym.txt # 다른 inode 번호
Q2. 프로세스와 스레드의 차이점은?
프로세스: 독립적인 메모리 공간을 가지며, IPC(파이프, 소켓 등)로 통신합니다. 하나의 프로세스 충돌이 다른 프로세스에 영향을 주지 않습니다.
스레드: 같은 프로세스 내에서 메모리를 공유합니다. 컨텍스트 스위칭이 빠르지만, 하나의 스레드 오류가 전체 프로세스에 영향을 줄 수 있습니다.
# 프로세스 확인
ps -eLf | head # 스레드(LWP) 포함 목록
# NLWP 열이 스레드 수를 나타냄
# 특정 프로세스의 스레드 수
ls /proc/PID/task/ | wc -l
Q3. 파일 권한 755와 644의 의미는?
755: rwxr-xr-x - 소유자(읽기/쓰기/실행), 그룹(읽기/실행), 기타(읽기/실행). 디렉토리와 실행 파일에 주로 사용합니다.
644: rw-r--r-- - 소유자(읽기/쓰기), 그룹(읽기), 기타(읽기). 일반 파일에 주로 사용합니다.
숫자 계산: r=4, w=2, x=1. 합산하여 각 위치(소유자/그룹/기타)를 표현합니다.
Q4. SIGTERM과 SIGKILL의 차이점은?
SIGTERM (15): 프로세스에 정상 종료를 요청합니다. 프로세스가 시그널 핸들러에서 정리 작업(파일 닫기, 임시 파일 삭제 등)을 수행한 후 종료할 수 있습니다. kill PID의 기본값입니다.
SIGKILL (9): 커널이 즉시 프로세스를 강제 종료합니다. 프로세스가 무시하거나 핸들링할 수 없습니다. 정리 작업 없이 즉시 종료되므로 데이터 손실 위험이 있습니다. 마지막 수단으로만 사용해야 합니다.
Q5. grep, awk, sed의 사용 시점은?
- grep: 패턴 검색이 목적일 때 (특정 문자열이 포함된 줄 찾기)
- awk: 필드 기반 데이터 처리가 필요할 때 (컬럼 추출, 계산, 조건 필터링)
- sed: 스트림 편집이 필요할 때 (문자열 치환, 줄 삭제/추가)
실전에서는 이 세 도구를 파이프로 연결하여 사용하는 경우가 많습니다.
# 로그에서 5xx 에러를 일으킨 URL의 빈도 분석
grep " 5[0-9][0-9] " access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head
Q6. 파이프(|)와 리다이렉트(>)의 차이점은?
파이프(|): 한 명령어의 표준 출력을 다른 명령어의 표준 입력으로 연결합니다. 프로세스 간 통신입니다.
리다이렉트(>): 명령어의 출력을 파일로 보냅니다. 프로세스와 파일 간 통신입니다.
# 파이프: ls 출력이 grep의 입력으로
ls -la | grep ".conf"
# 리다이렉트: ls 출력이 파일로
ls -la > filelist.txt
Q7. inode란 무엇인가?
inode는 파일 시스템에서 파일의 메타데이터를 저장하는 데이터 구조입니다. 파일 이름을 제외한 모든 정보(소유자, 권한, 크기, 타임스탬프, 데이터 블록 위치)를 포함합니다. 디렉토리 엔트리가 파일 이름과 inode 번호를 매핑합니다.
# inode 번호 확인
ls -i file.txt
stat file.txt # 상세 inode 정보
# inode 사용량 확인 (파일 수 제한)
df -i
Q8. SSH 터널링 3종류를 설명하세요.
-
로컬 포트 포워딩 (-L): 로컬 포트를 통해 원격 서비스에 접근.
ssh -L 5432:db:5432 bastion- 로컬의 5432로 접속하면 bastion을 거쳐 db의 5432에 연결. -
리모트 포트 포워딩 (-R): 원격에서 로컬 서비스에 접근.
ssh -R 8080:localhost:3000 server- server의 8080으로 접속하면 로컬의 3000에 연결. -
동적 포트 포워딩 (-D): SOCKS 프록시 생성.
ssh -D 1080 server- 로컬 1080을 SOCKS 프록시로 사용하여 server를 통해 모든 트래픽 전달.
Q9. 좀비 프로세스란 무엇이며 어떻게 처리하나요?
좀비 프로세스는 실행이 끝났지만 부모 프로세스가 wait() 시스템 콜로 종료 상태를 아직 수거하지 않은 프로세스입니다. PID만 차지하고 리소스는 사용하지 않지만, 많이 쌓이면 PID 고갈 문제가 발생합니다.
처리 방법: 부모 프로세스에 SIGCHLD를 보내거나, 부모 프로세스를 종료하면 init/systemd가 고아 프로세스를 인수하여 정리합니다.
ps aux | awk '$8=="Z"' # 좀비 프로세스 확인
kill -SIGCHLD PARENT_PID # 부모에게 자식 종료 알림
Q10. /proc 파일 시스템이란?
/proc는 커널과 프로세스 정보를 파일 형태로 제공하는 가상 파일 시스템입니다. 디스크에 실제로 존재하지 않으며, 커널이 실시간으로 생성합니다.
cat /proc/cpuinfo # CPU 정보
cat /proc/meminfo # 메모리 정보
cat /proc/PID/status # 특정 프로세스 상태
cat /proc/PID/fd/ # 열린 파일 디스크립터
cat /proc/loadavg # 시스템 부하
Q11. stdin, stdout, stderr의 파일 디스크립터 번호는?
- 0: stdin (표준 입력)
- 1: stdout (표준 출력)
- 2: stderr (표준 에러)
command > file.txt # 1번(stdout)을 파일로
command 2> error.log # 2번(stderr)을 파일로
command > file.txt 2>&1 # stderr를 stdout과 같은 곳으로
command < input.txt # 0번(stdin)을 파일에서 읽기
Q12. cron과 at의 차이점은?
cron: 반복적인 스케줄 작업에 사용합니다. crontab 파일로 관리합니다.
# 분 시 일 월 요일 명령어
crontab -e
0 2 * * * /backup/run.sh # 매일 새벽 2시
*/5 * * * * /check/health.sh # 5분마다
at: 일회성 작업에 사용합니다.
at now + 30 minutes
> /scripts/deploy.sh
> Ctrl+D
Q13. 로드 평균(Load Average)이란?
로드 평균은 실행 가능(Running) 또는 대기(Waiting) 상태인 프로세스의 평균 수입니다. uptime 명령어로 1분, 5분, 15분 평균을 확인합니다.
해석: CPU 코어 수와 비교합니다. 4코어 시스템에서 로드 평균 4.0이면 100% 활용, 8.0이면 과부하(프로세스가 대기 중)입니다.
uptime # load average: 2.50, 3.10, 2.80
nproc # CPU 코어 수 확인
Q14. swap 메모리란? 왜 서버에서는 주의해야 하나요?
Swap은 RAM이 부족할 때 디스크를 메모리처럼 사용하는 공간입니다. 디스크 I/O는 RAM보다 수천 배 느리므로, 서버가 swap을 활발히 사용하면 성능이 급격히 저하됩니다.
free -h # swap 사용량 확인
swapon --show # swap 디바이스 확인
vmstat 1 # si/so 값이 크면 swap 활발히 사용 중
cat /proc/sys/vm/swappiness # swap 사용 경향 (0-100, 낮을수록 RAM 우선)
Q15. set -euo pipefail은 무엇을 하나요?
Bash 스크립트의 안전 장치입니다.
- -e (errexit): 명령어가 실패(non-zero exit)하면 스크립트 즉시 중단
- -u (nounset): 선언되지 않은 변수 사용 시 에러 발생
- -o pipefail: 파이프라인에서 중간 명령어 실패도 감지
이 세 옵션 없이는 에러가 무시되어 예상치 못한 동작이 발생할 수 있습니다. 프로덕션 스크립트에서는 항상 사용하는 것이 권장됩니다.
18. 퀴즈
Q1. 다음 명령어의 출력 결과는? echo "Hello" | tee output.txt | wc -c
답: 6
tee는 stdin을 output.txt에 쓰면서 동시에 stdout으로 전달합니다. wc -c는 바이트 수를 세는데, "Hello\n" = 6바이트입니다.
Q2. chmod 4755 script.sh에서 4는 무엇을 의미하나요?
답: SetUID 비트
4는 SetUID(Set User ID) 비트입니다. 이 파일을 실행하면 파일 소유자의 권한으로 실행됩니다. 예를 들어 /usr/bin/passwd가 root 소유이면서 SetUID가 설정되어 있어 일반 사용자도 비밀번호를 변경할 수 있습니다. SetGID는 2, Sticky Bit는 1입니다.
Q3. 다음 중 파일을 실시간으로 모니터링하면서 특정 패턴만 필터링하는 올바른 방법은?
A) grep "ERROR" < tail -f app.log
B) tail -f app.log | grep "ERROR"
C) tail -f app.log > grep "ERROR"
D) cat app.log | tail -f | grep "ERROR"
답: B
tail -f의 출력을 파이프로 grep에 전달합니다. A는 문법 오류, C는 리다이렉트가 잘못 사용됨, D는 cat이 먼저 전체를 읽어 tail -f가 의미 없습니다.
Q4. ssh -L 3306:db-server:3306 bastion에서 실제로 일어나는 일은?
답: 로컬 머신의 3306 포트로 접속하면, SSH 연결을 통해 bastion 서버를 경유하여 db-server의 3306 포트(MySQL)에 연결됩니다. 로컬에서 mysql -h 127.0.0.1 -P 3306으로 원격 DB에 접속할 수 있습니다. 통신은 SSH로 암호화됩니다.
Q5. 다음 스크립트에서 버그를 찾으세요.
#!/bin/bash
for f in $(ls *.txt); do
mv "$f" "${f%.txt}.md"
done
답: ls 출력을 for 루프에 사용하면 파일 이름에 공백이 있을 때 깨집니다. 올바른 방법:
#!/bin/bash
for f in *.txt; do
[ -e "$f" ] || continue
mv "$f" "${f%.txt}.md"
done
glob 패턴을 직접 사용하고, 매칭 파일이 없을 때를 대비해 존재 확인을 합니다.
19. 참고 자료
공식 문서 및 매뉴얼
- GNU Coreutils Manual - 핵심 유틸리티 공식 문서
- GNU Bash Manual - Bash 공식 레퍼런스
- The Linux Documentation Project - Linux 종합 문서 프로젝트
- man7.org Linux Manual Pages - Linux man page 온라인 버전
- tmux Wiki - tmux 공식 위키
학습 리소스
- Linux Journey - 초보자 친화적 Linux 학습 사이트
- OverTheWire Bandit - 게임으로 배우는 Linux 명령어
- Vim Adventures - 게임으로 배우는 Vim
- ExplainShell - 명령어를 입력하면 각 부분을 설명해주는 도구
- ShellCheck - Bash 스크립트 린터/분석기
도서
- The Linux Command Line (William Shotts) - Linux CLI 바이블
- Linux Pocket Guide (Daniel J. Barrett) - 빠른 참조용
- Bash Cookbook (Carl Albing) - Bash 레시피 모음
- sed and awk (Dale Dougherty) - 텍스트 처리 심화
치트시트
- devhints.io/bash - Bash 치트시트
- tmux Cheat Sheet - tmux 단축키 모음
- Vim Cheat Sheet - Vim 명령어 모음