Docker로 돌아가는 Transmission, “목록에서 사라진” 파일만 골라서 지우기

문제 상황

토렌트 클라이언트를 오래 쓰다 보면 흔히 겪는 문제가 있다. 다운로드가 끝난 뒤 토렌트를 목록에서 제거하면서 “데이터도 함께 삭제”를 누르지 않는 경우, 파일은 디스크에 그대로 남는다. 시간이 지나면 디스크에는

  • Transmission이 여전히 관리 중이고 시딩 목적으로 남겨둔 파일
  • Transmission 목록에서는 이미 제거됐지만 디스크에 고아처럼 남은 파일

이 두 종류가 뒤섞여 쌓인다. 후자만 골라서 지우고 싶은데, 파일명만 보고는 구분이 안 된다는 게 문제였다.

환경

  • Docker로 구동 중인 linuxserver/transmission 이미지
  • 웹 UI 대신 서버 콘솔(SSH)로 직접 관리
  • 다운로드 디렉토리는 컨테이너 내부 /torrent, 호스트에는 별도 경로로 바인드 마운트

핵심 아이디어

Transmission이 “현재 알고 있는” 항목 이름 목록과, 실제 다운로드 폴더에 있는 파일/폴더 이름 목록을 비교(diff) 하면 된다.

  • 두 목록에 공통으로 있는 것 → 아직 관리 중, 손대면 안 됨
  • 디스크에만 있고 Transmission 목록에는 없는 것 → 목록에서 제거된 고아 파일, 삭제 대상

1단계 — 컨테이너 확인

docker ps | grep -i transmission

linuxserver/transmission 이미지는 컨테이너 안에 CLI 도구(transmission-remote)가 이미 포함되어 있어서, 호스트에 별도 설치 없이 docker exec로 바로 사용할 수 있다.

2단계 — 다운로드 경로 확인

컨테이너 설정 파일에서 실제 다운로드 경로를 확인한다.

docker exec transmission cat /config/settings.json | grep -E "download-dir|incomplete-dir"

바인드 마운트 정보와 대조해서 호스트 쪽 실제 경로를 특정한다.

docker inspect transmission --format '{{ json .Mounts }}' | python3 -m json.tool

3단계 — 이름 비교

주의할 점: 토렌트 이름에는 공백, 아포스트로피, 대괄호, 마침표 등 특수문자가 흔히 섞여 있다. 간단한 목록 출력(-l)은 컬럼이 잘리거나 긴 이름이 축약되므로, -t all -i 옵션으로 상세 정보를 뽑아 Name: 필드만 추출하는 방식이 훨씬 안전하다.

HOST_DL_DIR="/실제/호스트/다운로드/경로"

# 1. Transmission이 현재 관리 중인 전체 이름
docker exec transmission transmission-remote localhost:9091 -n '계정:비밀번호' -t all -i \
  | grep "^  Name: " | sed 's/^  Name: //' | sort > /tmp/tracked_names.txt

# 2. 디스크 최상위 목록
ls -1 "$HOST_DL_DIR" | sort > /tmp/disk_names.txt

# 3. 목록엔 없는데 디스크엔 있는 것 = 삭제 후보
comm -13 /tmp/tracked_names.txt /tmp/disk_names.txt > /tmp/orphaned_candidates.txt

comm -13은 “두 번째 파일에만 있는 줄”을 뽑아주는 명령이라, 정확히 “Transmission 목록엔 없고 디스크엔 있는 것”만 골라낸다.

4단계 — 용량 확인 후 삭제

바로 지우지 않고, 먼저 용량부터 눈으로 확인한다.

while read -r name; do
  du -sh "$HOST_DL_DIR/$name" 2>/dev/null
done < /tmp/orphaned_candidates.txt

이상이 없으면 삭제.

while read -r name; do
  echo "삭제: $name"
  rm -rf "$HOST_DL_DIR/$name"
done < /tmp/orphaned_candidates.txt

스크립트로 묶기

매번 명령어를 나열하는 대신, 확인 절차(y/n)까지 포함한 스크립트로 저장해두면 편하다.

#!/bin/bash
HOST_DL_DIR="/실제/호스트/다운로드/경로"

docker exec transmission transmission-remote localhost:9091 -n '계정:비밀번호' -t all -i \
  | grep "^  Name: " | sed 's/^  Name: //' | sort > /tmp/tracked_names.txt
ls -1 "$HOST_DL_DIR" | sort > /tmp/disk_names.txt
comm -13 /tmp/tracked_names.txt /tmp/disk_names.txt > /tmp/orphaned_candidates.txt

echo "=== 삭제 후보 ($(wc -l < /tmp/orphaned_candidates.txt)개) ==="
while read -r name; do
  du -sh "$HOST_DL_DIR/$name" 2>/dev/null
done < /tmp/orphaned_candidates.txt

echo ""
read -p "위 목록 삭제할까요? (y/n): " confirm
if [ "$confirm" = "y" ]; then
  while read -r name; do
    echo "삭제: $name"
    rm -rf "$HOST_DL_DIR/$name"
  done < /tmp/orphaned_candidates.txt
  df -h "$HOST_DL_DIR"
else
  echo "취소됨"
fi

실행 파일로 만들어두면 다음부터는 스크립트 한 번 돌리는 것으로 끝난다.

chmod +x clean_transmission.sh
./clean_transmission.sh

주의할 점

  • 아직 다운로드 진행 중인 파일은 incomplete-dir이 다운로드 완료 경로와 같으면 임시 파일이 섞여 있을 수 있으니, 진행 중 항목이 있다면 삭제 후보에 잘못 걸리지 않았는지 한 번 더 확인하는 게 안전하다.
  • 스크립트를 처음 도입할 때는 rm -rf 전에 반드시 후보 목록과 용량을 눈으로 검토하는 단계를 거치는 걸 추천한다. 자동화는 확인 절차가 검증된 이후에 믿고 맡기는 게 좋다.
  • 컨테이너 인증 정보(계정/비밀번호)는 스크립트에 평문으로 남기지 말고, 환경변수나 별도 설정 파일로 분리하는 것도 고려할 만하다.

마무리

토렌트 클라이언트를 오래 운영하다 보면 “지워도 되는지 애매한 파일”이 쌓이기 마련이다. 파일명이나 수정 날짜로 추측하는 대신, 클라이언트가 실제로 추적 중인 항목과 디스크 상태를 기계적으로 비교하면 실수 없이 정리할 수 있다. comm 하나로 끝나는 간단한 아이디어지만, 반복 작업을 스크립트로 묶어두면 다음번엔 고민 없이 한 번의 실행으로 끝난다.