리눅스 명령어

tar 압축과 해제 옵션 정리, czvf xzvf 순서까지 헷갈리지 않게

tar 압축과 해제 옵션 정리, czvf xzvf 순서까지 헷갈리지 않게

서버를 조금이라도 만져 봤다면 .tar.gz 파일은 피할 수 없습니다. 워드프레스 플러그인을 수동으로 올릴 때도, 호스팅을 옮기면서 public_html 통째로 백업할 때도, 소스를 내려받아 빌드할 때도 항상 이 확장자가 붙어 있습니다. 그런데 막상 터미널 앞에 앉으면 czvf였는지 xzvf였는지가 기억나지 않습니다. 잘못 치면 애써 만든 압축 파일을 덮어써 버리는 일도 생깁니다.

이게 헷갈리는 이유는 옵션을 통째로 외우려 하기 때문입니다. czvf는 하나의 단어가 아니라 글자 네 개가 붙은 덩어리입니다. 글자마다 뜻이 따로 있고, 그 뜻을 알면 상황에 맞게 조립할 수 있습니다. 외우는 게 아니라 조립하는 쪽으로 머리를 바꾸면 다시는 헷갈리지 않습니다.

한눈에 · tar가 하는 일 이해 → 옵션 글자(c/x/t/z/v/f) 분해 → 상황별 완성 명령 복사 → gz·bz2·xz 선택 → 에러 메시지별 대응 → 백업 스크립트로 굳히기

tar는 압축 도구가 아니라 ‘묶는’ 도구입니다

먼저 오해 하나를 풀고 가야 합니다. tar 자체는 파일을 작게 만들지 않습니다. tar는 “tape archive”의 줄임말로, 옛날에 테이프에 파일을 순서대로 이어 붙여 저장하던 도구에서 왔습니다. 여러 파일과 디렉터리를 하나의 파일로 이어 붙이는 것까지가 tar의 일이고, 실제로 용량을 줄이는 건 gzip·bzip2·xz라는 별도 프로그램입니다.

그래서 확장자가 두 겹으로 붙습니다. backup.tar는 묶기만 한 상태입니다. 여기에 gzip을 돌리면 backup.tar.gz가 됩니다. bzip2를 돌리면 .tar.bz2, xz를 돌리면 .tar.xz입니다. 파일 이름만 봐도 “묶은 다음 무엇으로 눌렀는지”가 그대로 드러납니다.

이 구조를 알면 옵션 조합이 자연스럽게 이해됩니다. z는 gzip을 붙이라는 뜻이고, j는 bzip2, J는 xz입니다. tar가 압축 프로그램을 대신 호출해 주는 겁니다.

그럼 zip을 쓰면 되지 않나 싶겠지만, 리눅스에서 tar가 계속 쓰이는 이유가 따로 있습니다. tar는 파일 권한(rwx), 소유자와 그룹, 심볼릭 링크를 그대로 보존합니다. 웹 디렉터리를 zip으로 옮겼다가 풀었더니 권한이 전부 틀어져서 500 에러가 나는 경우를 겪어 봤다면 이 차이가 얼마나 큰지 아실 겁니다. 권한 숫자가 헷갈린다면 chmod 755와 644의 실제 차이, 왜 폴더는 755여야 하나를 같이 보시면 정리가 됩니다.

옵션 글자 하나씩 뜯어보기 (c, x, t, z, v, f)

구조를 봤으니 이제 czvf 덩어리를 글자 단위로 잘라 보겠습니다. 표로 보면 한 번에 정리됩니다.

옵션뜻언제 쓰나
ccreate, 새로 만들기압축(묶기)할 때
xextract, 꺼내기해제할 때
tlist, 목록 보기안에 뭐가 있는지 확인할 때
zgzip 필터.tar.gz, .tgz
jbzip2 필터.tar.bz2
Jxz 필터 (대문자).tar.xz
vverbose, 처리 중인 파일 출력진행 상황 보고 싶을 때
f다음 인자를 파일명으로 사용사실상 항상

c, x, t 셋 중 하나는 반드시 들어갑니다. 이게 “무엇을 할지”를 정하는 동사 자리입니다. 나머지는 옵션입니다.

그리고 f는 반드시 맨 뒤에 와야 합니다. f 바로 다음에 오는 인자를 아카이브 파일 이름으로 받기 때문입니다. tar czfv backup.tar.gz dir처럼 쓰면 tar는 v를 파일 이름으로 착각하고 엉뚱한 파일을 만듭니다. 그래서 관례적으로 czvf, xzvf처럼 f를 끝에 붙입니다.

압축할 때 쓰는 조합

만들 거니까 c, gzip으로 누를 거니까 z, 진행 보고 싶으니 v, 파일 이름 줄 거니까 f. 합쳐서 czvf입니다.

해제할 때 쓰는 조합

꺼낼 거니까 x, 나머지는 같습니다. xzvf입니다. 참고로 요즘 GNU tar는 압축 형식을 자동으로 감지합니다. 그래서 tar xvf backup.tar.gz처럼 z를 빼도 대부분 잘 풀립니다. 다만 오래된 시스템이나 BusyBox 기반의 최소 환경에서는 자동 감지가 안 될 수 있으니, 확신이 없으면 형식을 명시하는 편이 안전합니다.

풀기 전에 내용부터 확인하는 t 옵션

받은 압축 파일을 바로 푸는 건 위험합니다. 안에 최상위 디렉터리가 없으면 파일 수백 개가 현재 위치에 그대로 쏟아집니다. 그래서 저는 항상 목록부터 봅니다.

tar tzvf backup.tar.gz | head -20

t가 목록, z가 gzip, v가 권한·크기·날짜까지 자세히, f가 파일 지정입니다. head -20으로 앞부분만 보면 최상위 구조를 바로 알 수 있습니다.

가장 많이 쓰는 세 가지
가장 많이 쓰는 세 가지

실제로 자주 쓰는 명령어 모음

글자 의미를 알았으니 이제 그대로 복사해 쓸 수 있는 완성형 명령을 보겠습니다.

압축하기

디렉터리 하나를 통째로 묶는 기본형입니다.

tar czvf backup.tar.gz /path/to/dir

예를 들어 웹 루트를 백업한다면 이렇게 됩니다.

cd /var/www
tar czvf html-backup.tar.gz html
ls -lh html-backup.tar.gz

cd로 상위로 이동한 뒤 상대경로로 묶는 걸 권합니다. 절대경로를 그대로 넣으면 경고가 뜨고(뒤에서 설명합니다), 나중에 풀 때 경로 구조가 예상과 달라집니다. 그리고 만든 직후엔 ls -lh로 크기를 확인하는 습관을 들이세요. 0바이트에 가까운 파일이 나왔다면 뭔가 잘못된 겁니다.

해제하기

현재 디렉터리에 그냥 푸는 경우입니다.

tar xzvf backup.tar.gz

다른 경로에 풀고 싶으면 -C(change directory) 옵션을 씁니다.

tar xzvf backup.tar.gz -C /target/dir

-C로 지정한 디렉터리는 미리 존재해야 합니다. 없으면 tar: /target/dir: Cannot open: No such file or directory가 뜹니다. mkdir -p /target/dir로 먼저 만들어 두세요.

일부만 꺼내기

전체를 풀 필요 없이 파일 하나만 복구하고 싶을 때가 있습니다. 설정 파일 하나 날렸을 때가 딱 그렇습니다.

tar xzvf backup.tar.gz html/wp-config.php

경로는 아카이브 안에 저장된 그대로 적어야 합니다. tar tzvf로 먼저 확인한 경로를 복사해 쓰는 게 확실합니다. 패턴으로 여러 개를 꺼낼 때는 --wildcards를 붙입니다.

tar xzvf backup.tar.gz --wildcards 'html/*.php'

특정 파일 제외하기

node_modules나 로그 디렉터리는 백업에 넣을 이유가 없습니다. 용량만 잡아먹습니다.

tar czvf project.tar.gz --exclude='node_modules' --exclude='*.log' project/

여기서 자주 걸리는 함정이 있습니다. --exclude는 대상 디렉터리 이름보다 앞에 와야 합니다. 뒤에 쓰면 무시되는 경우가 있습니다. 그리고 패턴은 따옴표로 감싸세요. 안 그러면 쉘이 먼저 *.log를 확장해 버려서 의도와 다르게 동작합니다.

진행 상황 보기

수십 기가짜리 백업에서 v를 켜면 파일 이름이 화면에 계속 쏟아지면서 오히려 느려질 수 있습니다. 이럴 땐 v를 빼고 체크포인트를 씁니다.

tar czf backup.tar.gz --checkpoint=1000 --checkpoint-action=dot /var/www

1000개 처리할 때마다 점 하나를 찍습니다. 화면이 조용하면서도 살아 있다는 건 알 수 있습니다.

gz, bz2, xz 중 뭘 쓸까

명령은 익혔는데 z, j, J 중에 뭘 고를지에서 다시 멈추는 분이 많습니다. 정답은 상황에 따라 다르지만, 경향은 분명합니다.

형식옵션압축률압축 속도CPU/메모리호환성
gzipz보통빠름낮음가장 넓음
bzip2j좋음느림중간넓음
xzJ가장 좋음가장 느림높음보통

구체적인 수치는 데이터 종류에 따라 크게 달라집니다. 텍스트 로그는 어느 쪽으로도 잘 줄고, 이미 압축된 JPG나 mp4는 뭘 써도 거의 안 줄어듭니다. 그래서 “xz가 몇 % 더 좋다”는 식의 숫자는 믿을 게 못 됩니다.

실무에서 중요한 건 다른 지점입니다. 공유 웹호스팅처럼 CPU와 메모리에 제한이 걸린 환경에서 J로 큰 디렉터리를 압축하면 시간이 한참 걸리거나, 도중에 프로세스가 강제 종료됩니다. SSH 세션이 끊기면서 작업이 통째로 날아가기도 합니다. 그런 환경에서는 그냥 z를 쓰는 게 낫습니다.

받는 쪽 사정도 고려해야 합니다. 상대 서버에 xz 유틸리티가 안 깔려 있으면 그쪽에서 풀지 못합니다. 어디서든 문제없이 풀리는 걸 우선한다면 답은 여전히 gzip입니다. 아카이브 보관처럼 시간이 충분하고 용량이 아까운 경우에만 xz를 고려하세요.

자주 막히는 지점들

명령을 맞게 쳤는데 결과가 이상할 때가 있습니다. 에러 메시지별로 정리해 두면 다음에 당황하지 않습니다.

tar: Removing leading '/' from member names

절대경로로 압축할 때 나오는 메시지입니다. 에러가 아니라 경고입니다. tar가 /var/www/html을 var/www/html로 바꿔서 저장했다는 뜻입니다. 나중에 아무 데서나 풀어도 시스템 루트를 덮어쓰지 않게 하는 안전장치입니다. 대신 푼 위치 기준으로 var/www/html 구조가 생기니, 원래 자리에 되돌리려면 tar xzvf backup.tar.gz -C /처럼 루트를 지정해야 합니다.

tar: Error is not recoverable: exiting now

보통 앞에 다른 줄이 같이 뜹니다. gzip: stdin: not in gzip format이 함께 나왔다면 형식을 잘못 지정한 겁니다. 확장자가 .gz인데 실제로는 그냥 tar이거나 zip인 경우가 의외로 많습니다. 확인은 file 명령으로 합니다.

file backup.tar.gz
# backup.tar.gz: gzip compressed data, from Unix
# 또는
# backup.tar.gz: POSIX tar archive (GNU)

POSIX tar archive로 나왔다면 z를 빼고 tar xvf로 풀면 됩니다. 아무 형식도 못 알아보고 data로만 나온다면 파일이 전송 중에 깨졌을 가능성이 큽니다. 다시 받아 보세요.

tar: Cannot open: Permission denied

쓰기 권한이 없는 경로에 풀려고 할 때 나옵니다. 지금 사용자가 그 디렉터리에 쓸 수 있는지 ls -ld /target/dir로 확인하세요. 필요하면 sudo를 붙이되, 무턱대고 붙이기 전에 소유자 문제부터 확인하는 게 낫습니다.

풀었더니 파일이 사방에 흩어진 경우

아카이브 최상위에 디렉터리가 없고 파일이 바로 들어 있는 구조입니다. 이미 벌어진 뒤엔 되돌리기가 번거롭습니다. 그래서 낯선 아카이브는 빈 디렉터리를 하나 만들어 거기에 푸는 습관이 안전합니다.

mkdir -p /tmp/unpack && tar xzvf backup.tar.gz -C /tmp/unpack
ls /tmp/unpack

root로 풀었더니 소유자가 이상해진 경우

root로 압축을 풀면 tar는 아카이브에 기록된 원래 소유자(UID/GID)를 그대로 복원합니다. 다른 서버에서 만든 아카이브라면 그 서버의 UID가 지금 서버의 엉뚱한 사용자와 겹칠 수 있습니다. 현재 사용자 소유로 풀고 싶으면 이렇게 합니다.

tar xzvf backup.tar.gz --no-same-owner -C /target/dir

심볼릭 링크 처리

기본적으로 tar는 심볼릭 링크를 링크 그대로 저장합니다. 링크가 가리키는 실제 파일 내용을 담고 싶으면 -h(dereference)를 붙입니다. 다만 링크가 큰 디렉터리를 가리키고 있으면 아카이브 용량이 폭증하니 주의하세요. 하드링크는 tar가 알아서 감지해 중복 저장하지 않습니다.

낯선 아카이브를 안전하게 푸는 순서
낯선 아카이브를 안전하게 푸는 순서

한 걸음 더: 백업 스크립트로 굳히기

여기까지 손에 익었다면 매번 손으로 치는 대신 반복 작업으로 만들 차례입니다. 시작은 파일 이름에 날짜를 넣는 것부터입니다.

tar czf /backup/www-$(date +%Y%m%d).tar.gz -C /var/www html

$(date +%Y%m%d)는 실행 시점의 날짜를 20260903 형태로 넣어 줍니다. 이렇게만 해도 어제 백업을 오늘 백업이 덮어쓰는 사고를 막을 수 있습니다.

같은 서버에만 백업을 두면 디스크가 죽을 때 같이 죽습니다. 원격으로 바로 보내려면 파이프를 씁니다.

tar czf - /var/www/html | ssh user@host 'cat > backup.tar.gz'

f -는 결과를 파일 대신 표준 출력으로 흘려보내라는 뜻입니다. 그걸 SSH로 넘겨서 상대 서버가 cat으로 받아 파일에 씁니다. 중간에 임시 파일을 만들지 않으니 디스크 여유가 없는 서버에서도 백업을 넘길 수 있습니다. 이 명령을 매번 쓸 거라면 호스트 정보를 미리 등록해 두는 편이 편합니다. SSH config 파일로 서버 별칭 등록하기, ssh 서버이름 한 줄로 접속하는 설정을 참고해 별칭을 만들어 두면 ssh backup-server 한 줄로 줄어듭니다.

cron에 걸 때는 주의할 게 하나 있습니다. cron은 로그인 쉘과 환경변수가 달라서 PATH가 최소한으로 잡혀 있습니다. tar, date, ssh 모두 /usr/bin/tar처럼 절대경로로 쓰거나, 스크립트 맨 위에 PATH를 직접 지정하세요. “손으로는 되는데 cron에서만 안 된다”의 절반은 이 문제입니다.

다음에 할 일 체크리스트

  • [ ] 지금 서버에서 tar czvf test.tar.gz 로 작은 디렉터리 하나를 묶어 보고 ls -lh 로 크기 확인
  • [ ] 그 파일을 tar tzvf 로 열어 최상위 구조가 어떻게 생겼는지 눈으로 확인
  • [ ] 빈 디렉터리를 만들어 -C 로 풀어 보고, 원본과 권한이 같은지 ls -l 비교
  • [ ] 백업에서 빼야 할 디렉터리(node_modules, logs, 캐시) 목록을 정리해 --exclude 조합 만들어 두기
  • [ ] 서버 CPU 여유를 보고 gz / bz2 / xz 중 기본으로 쓸 형식 하나 정하기
  • [ ] 날짜가 붙는 백업 명령을 스크립트 파일로 저장하고, 절대경로로 바꿔 cron에 등록
  • [ ] 만들어진 백업을 실제로 다른 경로에 풀어서 복원 테스트

마지막 항목이 제일 중요합니다. 복원해 본 적 없는 백업은 백업이 아니라 그냥 큰 파일입니다. 정말 필요한 순간에 Error is not recoverable을 보고 싶지 않다면, 한가할 때 한 번 풀어 보세요.

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다