파일 확장자별로 디렉터리를 분류하고 각 디렉터리의 전체 크기와 개수를 가져옵니다.

파일 확장자별로 디렉터리를 분류하고 각 디렉터리의 전체 크기와 개수를 가져옵니다.

디렉터리(예: /home/various/)와 여러 하위 디렉터리(예: /home/various/foo//home/various/ber/)가 있습니다./home/various/kol//home/various/whatever/

각 파일 확장자의 내용을 분석하여 총계를 표시하는 명령을 실행할 수 있습니까?

  • 전체 크기
  • 파일 수

터미널에 모든 파일 확장자를 수동으로 입력하고 싶지 않다고 가정해 보겠습니다. 부분적으로는 내부의 모든 파일 확장자를 (반복적으로) 모르기 때문입니다 /various/.

다음과 같은 출력이 좋을 것입니다.

*.txt 23 files, 10.2MB
*.pdf 8 files, 23.2MB
*.db 3 files, 2.3MB
*.cbz 24 files, 2.3GB
*.html 2,508 files, 43.9MB
*.readme 13 files, 4KB

답변1

기본 코드

duext() {

case "$1" in
  -* )
    set "./$1"
esac   

POSIXLY_CORRECT= find "${1-.}" -type f -exec du {} + | awk '
{
  sz=$1
  $1=""
  sub("^ *","")
  sub("^.*/","")
  sub("^\\.","")
  w=split($0,a,".")
  e=tolower(w==1?"*":"*."a[w])
  s[e]+=sz
  n[e]+=1
}
END {
  for (e in s) print 512*s[e]"\t"n[e]"\t"e
}'
}

용법: duext path. 기본값 path은 입니다 .. 이 함수는 sh호환되는 쉘에서 실행되어야 합니다.

이 함수는 다음 형식의 줄을 생성합니다.

s<tab>n<tab>e

여기서 s는 사용된 디스크 크기(바이트), n는 파일 수, e는 확장자입니다. 구문 분석을 최적화하기로 결정했기 때문에 요청한 출력과 다릅니다. "확장자"라고 부르는 것은 *nix의 파일 이름의 일부일 뿐입니다. 파일 이름에는 공백이나 탭 문자가 포함될 수 있습니다. e줄 끝에 (공백이나 탭이 포함될 수 있음) 배치하면 다른 필드를 안정적으로 식별할 수 있습니다. 예를 들어 다음과 같이 크기별로 쉽게 정렬할 수 있습니다.

duext /home/various/ | sort -rn -k1,1       # optionally: … | column -t

노트:

  • 경로 이름에 줄바꿈을 사용하면 잘못된 결과가 발생합니다.
  • POSIXLY_CORRECT= du …사용된 디스크 크기를 가져오는 이식 가능한 방법입니다. 512바이트 단위로 보고되므로 코드 후반부에 512*s[e]위치합니다 . awkGNU는 du몇 가지 흥미로운 옵션(예 --apparent-size: awk코드 조정이 필요할 수 있음)을 제공합니다.
  • sub("^\\.","")이름의 선행 점을 확장 구분 기호로 처리하지 않는 역할을 담당합니다. 실제로 .nfo는 확장자가 nfo. 이것이 원하는 것이 아니라면 이 줄을 제거하십시오.
  • 코드는 빈 확장(예: foo.)과 확장이 없는( foo)을 구별합니다. 전자는 로 보고되고 *., 후자는 로 보고됩니다 *.
  • 코드는 대소문자를 구분하지 않습니다. tolower대소문자를 구분하려면 제거하세요 .
  • 하드 링크로 인해 결과가 왜곡될 수 있습니다. du파일이 일부 기록된 파일에 대한 하드 링크인 경우 해당 파일을 무시할 수도 있고 무시하지 않을 수도 있습니다. 또한 (을 방지하기 위해 ) 필요한 만큼 여러 번 find … -exec du {} +실행하면 하드 링크된 파일이 동일한 파일로 전달될 수도 있고 전달되지 않을 수도 있습니다 . (GNU의 이식 불가능한 옵션 ) 을 사용하거나 다음을 실행 하여 각 파일을 강제로 계산할 수 있습니다. 파일당 하나. 하드 링크: . 하드 링크를 한 번만 안정적으로 계산하려면 다른 접근 방식(GNU의 단일 인스턴스 및 ?)이 필요합니다. 일반적으로 다른 확장자를 가진 하드 링크를 갖는 것이 가능합니다. 각 하드 링크를 개별적으로 계산하려는 경우에는 문제가 되지 않지만, 하나의 파일로 계산하려는 경우 할당된 확장자는 정의되지 않습니다.duargument list too longdudu -ldudufind … -exec du {} \;du--files0-from=

맞춤 형식

MB당신이 말하는 것인지 잘 모르겠습니다메가바이트 또는 메가바이트, 내 생각엔 후자인 것 같아. 다음 코드는 원하는 형식으로 변환되어야 합니다.

yourformat() { awk '
  function human(x) {
    if (x<1000) {return x} else {x/=1000}
    s="kMGTEPZY";
    while (x>=1000 && length(s)>1)
      {x/=1000; s=substr(s,2)}
    return int(10*x+0.5)/10 substr(s,1,1)
  }
  {
    s=$1; n=$2
    $1=""; $2=""
    sub("^  ","")
    print $0" "n" file"(n==1?"":"s")", "human(s)"B"
  }'
}

(참고: human(x)에서 발췌이 답변그리고 조정을 했습니다. )

다음과 같이 사용하세요:

duext /home/various/ | yourformat

duext내부적으로 사용되었으므로 awk이제 파이프를 사용하여 yourformat사용합니다 awk. 일반적으로 awk단일 함수에서 대신 사용할 수 있습니다. 여전히 별도의 s를 사용하면 예 를 들어 단일 쉘 함수 또는 함수 간 파이프라인에 awk넣을 수 있습니다 . (또는 적어도 GNU에서) sort …일종의 정렬을 구현하는 것이 가능하지만 바퀴를 재발명하는 것은 의미가 없습니다. IMO는 첫 번째 출력을 구문 분석하기 쉽게 유지하는 것이 옳은 일입니다. 그러니 신청하시면 됩니다awkawkawk어느필터링 및 서식 지정은 나중에 수행됩니다.

column -t사용할 수 있도록 형식을 개선해 보겠습니다 . 1024의 인수는 어떻습니까?

myformat() { awk '
  function human(x) {
    if (x<1000) {return x" "} else {x/=1024}
    s="kMGTEPZY";
    while (x>=1000 && length(s)>1)
      {x/=1024; s=substr(s,2)}
    return int(10*x+0.5)/10" "substr(s,1,1)"i"
  }
  {
    s=$1; n=$2
    $1=""; $2=""
    sub("^  ","")
    print $0"\t"n" file"(n==1?"":"s")"\t"human(s)"B"
  }'
}

그런 다음:

duext /home/various/ | sort -nr -k1,1 | myformat | column -t -s "$(printf '\t')"

노트:

  • "$(printf '\t')"탭 문자를 얻는 이식 가능한 방법입니다. Bash와 같은 일부 셸에서는 $'\t'동일한 작업이 수행됩니다 .
  • column그 자체로는 이식성이 없습니다.
  • 탭 문자가 포함된 확장자는 서식을 손상시킵니다. 그러나 그들은 매우 드뭅니다.

솔직히 저는 이 솔루션이 정말 마음에 들어서 계속 유지하겠습니다. due나중에 사용할 수 있도록 스크립트를 만들었습니다 .

#!/bin/sh

duext() {
}

myformat {
}

duext "${1-.}" | sort -nr -k1,1 | myformat | column -t -s "$(printf '\t')"

답변2

이것은 매우 흥미로운 문제이며 제가 만들 수 있는 최선의 스크립트는 다음과 같습니다.

set -e
# set -x

folder=$1
counter=$(tempfile)

# List file extensions
list_extensions() {
  find "$folder" -type f |
  while read filename
  do
    basename=${filename##*/}
    ext=${basename##*.}
    echo ${ext,,}  # downcase extensions to prevent duplicates
  done |
  sort -u
}

list_extensions |
while read extension
do
  size=$(find "$folder" -type f -iname "*.$extension" -fprintf $counter . -print0 |
    du -hc --files0-from=- | tail -n 1 | sed -E 's/\s+total//')
  count=$( wc -c < $counter )
  printf "*.%-10s\t%6s files\t%10s\n" "$extension" "$count" "$size"
done

rm $counter

복잡한 파일 이름을 지원하지 않고 예외가 많이 발생할 수 있으며 성능이 그다지 좋지는 않지만 작동합니다.

예제 출력:

*.wma              122 files          411M
*.wpl               16 files           64K
*.xls                2 files           24K
*.xlsx               1 files           28K
*.zip                5 files          333M

관련 정보