디스크 사용량에 대한 폴더의 고유한 기여

디스크 사용량에 대한 폴더의 고유한 기여

일일 스냅샷 폴더가 포함된 백업이 있습니다. 공간을 절약하기 위해 서로 다른 스냅샷에 있는 동일한 파일은 하드 링크(rsync로 생성됨)를 통해 중복 제거됩니다.

공간이 부족할 때 한 가지 옵션은 오래된 스냅샷을 삭제하는 것입니다. 그러나 하드 링크로 인해 특정 스냅샷을 삭제하여 얻을 수 있는 공간을 계산하기가 어렵습니다.

제가 생각할 수 있는 한 가지 옵션은 du -s먼저 모든 스냅샷 폴더에 사용하고 삭제한 폴더를 제외한 모든 폴더에 사용하는 것입니다. 그 차이를 통해 예상되는 확보 공간을 얻을 수 있습니다. 그러나 이는 매우 번거롭고 삭제할 올바른 스냅샷을 찾으려고 반복적으로 수행해야 합니다.

더 쉬운 방법이 있나요?


답을 고민하고 고민한 끝에스티븐 차제라스그리고드 로버트, 내 질문이 충분히 정확하지 않다는 것을 깨달았습니다. 보다 정확한 시도는 다음과 같습니다.

다른 스냅샷의 파일과 부분적으로 동일하게(하드 링크) 저장된 파일이 포함된 일련의 디렉터리("스냅샷")가 있습니다. 저는 스냅샷 목록과 그 안의 파일이 차지하는 사용된 각 디스크 스토리지의 양을 제공하지만 해당 스토리지가 다른 스냅샷의 파일에서도 사용되지 않도록 하는 솔루션을 찾고 있습니다. 각 스냅샷에 하드 링크가 존재할 가능성을 허용하고 싶습니다.

공간이 부족할 때 목록을 보고 어떤 스냅샷을 삭제해야 할지 결정할 수 있다는 아이디어입니다. 이는 삭제로 얻은 저장 공간과 스냅샷 값(예: 기간 기준) 사이의 절충안입니다.

답변1

GNU를 사용하여 수동으로 수행할 수 있습니다 find.

find snapshot-dir -type d -printf '1 %b\n' -o -printf '%n %b %i\n' |
   awk '$1 == 1 || ++c[$3] == $1 {t+=$2;delete c[$3]}
   END{print t*512}'

스냅샷 디렉토리에서 발견된 모든 링크를 찾은 후 링크 수가 0으로 떨어지는 파일의 디스크 사용량을 계산합니다.

find인쇄:

  • 1 <disk-usage>디렉토리용
  • <link-count> <disk-usage> <inode-number>다른 유형의 파일의 경우.

실제로는 그렇지 않고 항목 때문에 발생하며 해당 항목은 나열되지 않으며 디렉토리에는 일반적으로 다른 하드 링크가 없기 때문에 디렉토리의 링크 수가 항상 1이라고 가정 ..합니다 find.

이 출력에서 awk​​링크 수가 1인 항목의 디스크 사용량과 해당 항목 <link-count>이 표시된 횟수의 inode를 계산합니다(즉, 현재 디렉토리에 모든 하드 링크가 있는 항목이므로 링크) - 삭제된 후 디렉터리 트리의 공간이 1개로 회수됩니다.

합계) 스냅샷)을 사용할 수도 있습니다 find snapshot-dir1 snapshot-dir2.

스냅샷 디렉터리를 삭제할 때마다 절약되는 공간의 양(누적 방식)을 알고 싶다면 다음을 수행할 수 있습니다.

find snapshot-dir* \( -path '*/*' -o -printf "%p:\n" \) \
  -type d -printf '1 %b\n' -o -printf '%n %b %i\n' |
   awk '/:$/ {if (NR>1) print t*512; printf "%s ", $0; next}
        $1 == 1 || ++c[$3] == $1 {t+=$2;delete c[$3]}
        END{print t*512}'

스냅샷 목록을 어휘순으로 처리합니다. 다른 순서로 처리할 경우 마지막 번호(모든 스냅샷이 삭제된 경우)를 제외하고 다른 번호가 부여될 수 있습니다.

숫자를 더 읽기 쉽게 만들기를 참조하세요 numfmt.

모든 파일이 동일한 파일 시스템에 있다고 가정합니다. 그렇지 않은 경우 다음 %i으로 대체할 수 있습니다 %D:%i(모두 동일한 파일 시스템에 있지 않은 경우, 어쨌든 삭제할 수 없는 마운트 지점이 있음을 의미함).

답변2

파일 이름에 패턴 문자나 개행 문자가 포함되어 있지 않으면 find+ 의 제외 기능을 사용하여 du다음을 수행할 수 있습니다.

find -links +1 -type f \
    | cut -d/ -f2- \
    | du --exclude-from=- -s *

이 비트는 find하드 링크 수가 1( )보다 큰 모든 파일을 가져옵니다. 선행 찾기의 잘린 부분을 인쇄합니다. 그런 다음 여러 링크가 있는 모든 파일을 제외하고 각 디렉터리의 디스크 사용량을 요청합니다. 물론, 스냅샷을 삭제하면 이전에는 두 개의 링크가 있었던 파일에 이제 하나의 링크만 있는 파일이 있을 수 있습니다. 따라서 몇 번 삭제할 때마다 다시 실행해야 합니다.-type f-links +1cut./du

임의의 파일 이름을 사용해야 하는 경우 이를 대체하기 위해 더 많은 스크립트가 필요합니다 du(이것은 쉘 모드이므로 이스케이프가 불가능합니다).

게다가 Stéphane Chazelas가 지적했듯이 스냅샷 내부에 하드 링크가 있는 경우(모든 파일 이름은 스냅샷 간 하드 링크가 아닌 단일 스냅샷에 상주함) 해당 파일은 전체에서 제외됩니다(스냅샷이 삭제되더라도). 공간이 복원됩니다).

답변3

내가 이 답변을 쓴 이후로 Stéphane Chazelas는 그의 답변이 옳았다고 나에게 확신시켜 주었습니다. 나는 코드도 잘 작동하고 예쁜 인쇄 기능을 제공하기 때문에 코드를 포함하여 답변을 남길 것입니다. 출력은 다음과 같습니다.

              total               unique
--T---G---M---k---B  --T---G---M---k---B
     91,044,435,456          665,754,624  back-2018-03-01T06:00:01
     91,160,015,360          625,541,632  back-2018-04-01T06:00:01
     91,235,970,560          581,360,640  back-2018-05-01T06:00:01
     91,474,846,208          897,665,536  back-2018-06-01T06:00:01
     91,428,597,760          668,853,760  back-2018-07-01T06:00:01
     91,602,767,360          660,594,176  back-2018-08-01T06:00:01
     91,062,218,752        1,094,236,160  back-2018-09-01T06:00:01
    230,810,647,552       50,314,291,712  back-2018-11-01T06:00:01
    220,587,811,328          256,036,352  back-2018-11-12T06:00:01
    220,605,425,664          267,876,352  back-2018-11-13T06:00:01
    220,608,163,328          268,711,424  back-2018-11-14T06:00:01
    220,882,714,112          272,000,000  back-2018-11-15T06:00:01
    220,882,118,656          263,202,304  back-2018-11-16T06:00:01
    220,882,081,792          263,165,440  back-2018-11-17T06:00:01
    220,894,113,280          312,208,896  back-2018-11-18T06:00:01

두 가지 답변 모두에 100% 만족하지 않기 때문에(2018년 11월 18일 현재) 두 답변 모두에서 배운 내용에도 불구하고 나만의 도구를 만들어 여기에 게시했습니다.

~처럼스티븐 차제라스find답변은 inode 목록과 관련 파일/디렉토리 크기를 얻는 데 사용되지만 "최대 1개의 링크" 경험적 방법에 의존하지 않습니다. 대신, 각 입력 디렉터리에 대해 고유한 inode 목록(파일/디렉터리가 아님!)을 생성하고, 다른 디렉터리에서 inode를 필터링하고, 나머지 inode의 크기를 합산합니다. 이런 방식으로 각 입력 디렉터리의 가능한 하드 링크를 설명할 수 있습니다. 부작용으로 입력 디렉터리 집합 외부의 가능한 하드 링크를 무시합니다.

사용 된 Bash 외부 도구: find, xargs, mktemp, sort, tput, awk, tr, numfmt, touch, cat, comm. rm저도 알아요. 아주 가볍지는 않지만 제가 원하는 기능을 정확히 수행합니다. 다른 사람이 비슷한 요구 사항을 가지고 있는 경우를 대비하여 여기에서 공유하고 있습니다.

더 효율적이거나 완벽하게 만들 수 있는 것이 있으면 댓글을 남겨주세요! 나는 확실히 bash 전문가가 아닙니다.

이를 사용하려면 다음 코드를 스크립트 파일에 저장하세요 duu.sh. 첫 번째 주석 블록에는 간단한 사용 지침이 포함되어 있습니다.

#!/bin/bash

# duu
#
# disk usage unique to a directory within a set of directories
#
# Call with a list of directory names. If called without arguments,
# it operates on the subdirectories of the current directory.


# no arguments: call itself with subdirectories of .
if [ "$#" -eq 0 ]
then
    exec find . -maxdepth 1 -type d ! -name . -printf '%P\0' | sort -z \
        | xargs -r --null "$0"
    exit
fi


# create temporary directory
T=`mktemp -d`
# array of directory names
dirs=("$@")
# number of directories
n="$#"

# for each directory, create list of (unique) inodes with size
for i in $(seq 1 $n)
do
    echo -n "reading $i/$n: ${dirs[$i - 1]} "
    find "${dirs[$i - 1]}" -printf "%i\t%b\n" | sort -u > "$T/$i"
    # find %b: "The amount of disk space used for this file in 512-byte blocks."
    echo -ne "\r"
    tput el
done

# print header
echo "              total               unique"
echo "--T---G---M---k---B  --T---G---M---k---B"

# for each directory
for i in $(seq 1 $n)
do
    # compute and print total size
    #   sum block sizes and multiply by 512
    awk '{s += $2} END{printf "%.0f", s * 512}' "$T/$i" \
        | tr -d '\n' \
        | numfmt --grouping --padding 19
    echo -n "  "

    # compute and print unique size
    #   create list of (unique) inodes in the other directories
    touch "$T/o$i"
    for j in $(seq 1 $n)
    do
        if [ "$j" -ne "$i" ]
        then
            cat "$T/$j" >> "$T/o$i"
        fi
    done
    sort -o "$T/o$i" -u "$T/o$i"
    #   create list of (unique) inodes that are in this but not in the other directories
    comm -23 "$T/$i" "$T/o$i" > "$T/u$i"
    #   sum block sizes and multiply by 512
    awk '{s += $2} END{printf "%.0f", s * 512}' "$T/u$i" \
        | tr -d '\n' \
        | numfmt  --grouping --padding 19
    #   append directory name
    echo "  ${dirs[$i - 1]}"
done

# remove temporary files
rm -rf "$T"

관련 정보