디렉터리(예: /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]
위치합니다 .awk
GNU는du
몇 가지 흥미로운 옵션(예--apparent-size
:awk
코드 조정이 필요할 수 있음)을 제공합니다.sub("^\\.","")
이름의 선행 점을 확장 구분 기호로 처리하지 않는 역할을 담당합니다. 실제로.nfo
는 확장자가nfo
. 이것이 원하는 것이 아니라면 이 줄을 제거하십시오.- 코드는 빈 확장(예:
foo.
)과 확장이 없는(foo
)을 구별합니다. 전자는 로 보고되고*.
, 후자는 로 보고됩니다*
. - 코드는 대소문자를 구분하지 않습니다.
tolower
대소문자를 구분하려면 제거하세요 . - 하드 링크로 인해 결과가 왜곡될 수 있습니다.
du
파일이 일부 기록된 파일에 대한 하드 링크인 경우 해당 파일을 무시할 수도 있고 무시하지 않을 수도 있습니다. 또한 (을 방지하기 위해 ) 필요한 만큼 여러 번find … -exec du {} +
실행하면 하드 링크된 파일이 동일한 파일로 전달될 수도 있고 전달되지 않을 수도 있습니다 . (GNU의 이식 불가능한 옵션 ) 을 사용하거나 다음을 실행 하여 각 파일을 강제로 계산할 수 있습니다. 파일당 하나. 하드 링크: . 하드 링크를 한 번만 안정적으로 계산하려면 다른 접근 방식(GNU의 단일 인스턴스 및 ?)이 필요합니다. 일반적으로 다른 확장자를 가진 하드 링크를 갖는 것이 가능합니다. 각 하드 링크를 개별적으로 계산하려는 경우에는 문제가 되지 않지만, 하나의 파일로 계산하려는 경우 할당된 확장자는 정의되지 않습니다.du
argument list too long
du
du -l
du
du
find … -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는 첫 번째 출력을 구문 분석하기 쉽게 유지하는 것이 옳은 일입니다. 그러니 신청하시면 됩니다awk
awk
awk
어느필터링 및 서식 지정은 나중에 수행됩니다.
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