본문 바로가기
TYLER SONGBlog
← Blog index
CS학습5 min read

rm: 디렉터리 항목(링크)을 제거하는 파일 삭제 명령어

rm은 파일 시스템에서 파일 이름(디렉터리 항목)과 inode의 연결을 끊는 리눅스 명령어다. 휴지통 없이 즉시 삭제되므로 옵션의 의미를 정확히 알고 써야 한다. 이 글에서는 rm의 동작 원리, 주요 옵션, 안전하게 쓰는 방법을 정리한다.

By 송민성

1. 개념

rm(remove)은 파일 이름을 삭제하는 명령어다. 정확히는 디렉터리 안의 디렉터리 항목(directory entry)을 제거하고, 해당 inode의 링크 수(link count)를 1 감소시킨다. 링크 수가 0이 되고 그 파일을 연 프로세스도 없을 때 비로소 파일의 데이터 블록이 해제된다.

기본 형태는 다음과 같다.

shell
rm [옵션] 파일...

주요 옵션은 다음과 같다.

옵션의미
-r, -R디렉터리를 하위 내용까지 재귀적(recursive)으로 삭제
-f존재하지 않는 파일을 무시하고, 확인 질문을 하지 않음 (force)
-i삭제 전 매번 확인 (interactive)
-v삭제하는 항목을 출력 (verbose)
--이후 인자를 옵션이 아닌 파일명으로 취급

2. 왜 사용하는가

  • 불필요한 파일, 로그, 빌드 산출물, 임시 파일을 정리해 디스크 공간을 확보한다.
  • 쉘 스크립트와 CI/CD 파이프라인에서 삭제를 자동화한다.
  • GUI 없이 서버(SSH 접속 환경)에서 파일을 관리할 수 있는 기본 수단이다.

3. 동작 원리

리눅스 파일 시스템에서 파일 이름과 파일 내용은 분리되어 있다.

  1. 디렉터리는 "이름 → inode 번호" 매핑을 담은 특수한 파일이다.
  2. rm은 내부적으로 unlink() 시스템 콜(system call)을 호출해 이 매핑을 지우고 inode의 링크 수를 줄인다. 디렉터리를 지울 때는 먼저 내부 항목을 모두 지운 뒤 rmdir()을 호출한다.
  3. 링크 수가 0이 되고 열려 있는 파일 디스크립터(file descriptor)도 없으면 커널이 inode와 데이터 블록을 해제한다.

여기서 파생되는 특징은 다음과 같다.

  • 삭제 권한은 파일이 아니라 부모 디렉터리의 쓰기(write) 권한에 달려 있다. 이름을 지우는 일은 디렉터리를 수정하는 일이기 때문이다. (파일 자체가 읽기 전용이어도 부모 디렉터리에 쓰기 권한이 있으면 삭제할 수 있다. 이때 rm은 확인을 묻는다.)
  • 하드 링크(hard link)가 여러 개면 하나를 지워도 데이터는 남는다.
  • 어떤 프로세스가 파일을 열고 있으면 이름은 사라져도 프로세스가 닫을 때까지 공간은 반환되지 않는다.
  • 휴지통이 없다. 삭제 후 복구는 파일 시스템과 상황에 따라 보장되지 않는다.

4. 코드 예제

실습용 임시 디렉터리에서 실행해 보자.

shell
# 실습 디렉터리 준비 workdir=$(mktemp -d) cd "$workdir" # 1) 단일 파일 삭제 touch a.txt rm -v a.txt # 2) 존재하지 않는 파일: 오류 발생 vs -f로 무시 rm nothing.txt; echo "종료 코드: $?" # 오류 메시지, 종료 코드 1 rm -f nothing.txt; echo "종료 코드: $?" # 조용히 성공, 종료 코드 0 # 3) 디렉터리는 -r 필요 mkdir -p project/src touch project/src/main.py project/README.md rm project 2>&1 || echo "디렉터리는 -r 없이 삭제 불가" rm -rv project # 4) 하이픈으로 시작하는 파일명 touch -- -weird.txt rm -- -weird.txt # 5) 하드 링크: 링크 수 확인 echo "hello" > origin.txt ln origin.txt copy.txt ls -l origin.txt # 링크 수 2 rm origin.txt cat copy.txt # 데이터는 그대로 남아 있음 rm copy.txt # 6) 안전하게 삭제: 먼저 glob 결과를 확인 touch log1.tmp log2.tmp ls *.tmp # 대상 확인 rm -i *.tmp # 하나씩 확인 (y/n 입력) cd - >/dev/null rm -rf "$workdir"

5. 시간 복잡도 또는 성능 특성

  • 단일 파일 삭제는 unlink() 호출 한 번이며, 파일 크기와 거의 무관하다. 다만 큰 파일은 데이터 블록 해제 작업이 따라올 수 있어 파일 시스템에 따라 시간이 더 걸릴 수 있다.
  • rm -r은 하위 항목을 모두 순회하므로 삭제할 항목 수 n에 대해 최소 O(n)의 시스템 콜이 필요하다.
  • 파일이 수십만 개 이상인 디렉터리에서는 항목 수가 지배적인 비용이다. 이런 경우 find <경로> -type f -delete 등을 쓰기도 한다.

6. 실무 사용 사례

  • 빌드 산출물 정리: rm -rf node_modules dist
  • 오래된 로그 삭제: find /var/log/myapp -name "*.log" -mtime +30 -delete
  • 스크립트의 임시 파일 정리:
shell
tmpfile=$(mktemp) trap 'rm -f "$tmpfile"' EXIT echo "작업 중" > "$tmpfile"
  • Docker 이미지 빌드 시 같은 RUN 레이어에서 캐시 파일 삭제: RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

7. 주의할 점

  • 복구 불가를 전제로 하라. 삭제 전에 ls로 같은 패턴의 대상을 먼저 확인하는 습관이 필요하다.
  • 변수가 비어 있는 경우를 조심하라. rm -rf "$DIR"/*에서 DIR이 비어 있으면 /*가 되어 루트 아래 전체가 대상이 된다. 스크립트에서는 set -u를 쓰거나 ${DIR:?} 형태로 빈 값을 막는다.
shell
rm -rf "${BUILD_DIR:?BUILD_DIR가 비어 있음}"/*
  • 공백이 있는 경로는 반드시 따옴표로 감싼다. rm -rf $dir은 단어 분리(word splitting)로 의도하지 않은 경로를 지울 수 있다.
  • `-f`는 오류를 숨긴다. 존재 여부 오류뿐 아니라 확인 절차도 생략하므로 필요한 곳에서만 쓴다.
  • `rm`은 보안적 삭제가 아니다. 이름만 지우고 데이터를 덮어쓰지 않는다. 민감 정보 폐기는 별도 방법이 필요하다.
  • GNU rm은 기본적으로 / 삭제를 거부하지만(--preserve-root), 이 보호에 의존하면 안 된다.
  • 심볼릭 링크(symbolic link)에 rm을 쓰면 링크 자체만 삭제되고 대상 파일은 남는다. 단, rm -rf link/처럼 끝에 /를 붙이면 의도와 다르게 동작할 수 있으니 주의한다.

8. 핵심 정리

  • rm은 파일 데이터를 직접 지우는 것이 아니라 디렉터리 항목을 제거(unlink)하고 링크 수를 줄이는 명령이다.
  • 삭제 가능 여부는 대상 파일이 아닌 부모 디렉터리의 쓰기 권한이 결정한다.
  • 디렉터리는 -r, 확인 생략은 -f, 확인 요청은 -i, 과정 출력은 -v다.
  • 휴지통이 없으므로 대상 확인(ls), 변수 검증(${VAR:?}), 따옴표 사용이 기본 안전 수칙이다.
  • 하이픈으로 시작하는 파일명은 rm -- 파일명으로 지운다.