測量每個目錄的特定文件類型的磁碟使用情況(遞歸地,作為 ‘du –include’ 的展示)
這是我的工作程式碼,但我相信它沒有優化 - 必須有一種方法可以比這更快地完成工作:
find . -type f -iname '*.py' -printf '%h\0' | sort -z -u | xargs -r -0 -I{} sh -c ' find "{}" -maxdepth 1 -type f -iname "*.py" -print0 | xargs -r -0 du -sch | tail -1 | cut -f1 | tr "\n" " " echo -e "{}"' | sort -k1 -hr | head -50
目標是遞歸搜尋包含的所有目錄,
*.py
然後按每個目錄的名稱列印所有文件的總大小,*.py
按大小倒序排序,只顯示前 50 個。任何想法如何改進此程式碼(性能方面)但保持相同的輸出?
編輯:
我在以下範例上測試了您的提案:
47GB total: 5805 files
不幸的是,我無法將其進行比較,因為並非所有提案都遵循相同的準則:總大小應該是磁碟使用量,分隔符應該只有一個空格。格式應如下所示:numfmt --to=iec-i --suffix=B
以下 4 個是排序輸出,但 David 顯示的是文件的累積大小,而不是實際的磁碟使用情況。然而,他的進步是顯著的:快了 9.5 倍以上。Stéphane 和 Isaac 的程式碼是非常成功的贏家,因為他們的程式碼比參考程式碼快大約 32 倍。
$ time madjoe.sh real 0m2,752s user 0m3,022s sys 0m0,785s $ time david.sh real 0m0,289s user 0m0,206s sys 0m0,131s $ time isaac.sh real 0m0,087s user 0m0,032s sys 0m0,032s $ time stephane.sh real 0m0,086s user 0m0,013s sys 0m0,047s
不幸的是,下面的程式碼沒有排序也沒有顯示最大的 50 個結果(此外,在之前與 Isaac 的程式碼比較期間,下面的程式碼比 Isaac 的改進慢了大約 6 倍):
$ time hauke.sh real 0m0,567s user 0m0,609s sys 0m0,122s
要計算磁碟使用量而不是表觀大小的總和,您需要使用
%b
¹ 而不是%s
並確保每個文件只計算一次,例如:LC_ALL=C find . -iname '*.py' -type f -printf '%D:%i\0%b\0%h\0' | gawk -v 'RS=\0' -v OFS='\t' -v max=50 ' { inum = $0 getline du getline dir } ! seen[inum]++ { gsub(/\\/, "&&", dir) gsub(/\n/, "\\n", dir) sum[dir] += du } END { n = 0 PROCINFO["sorted_in"] = "@val_num_desc" for (dir in sum) { print sum[dir] * 512, dir if (++n >= max) break } }' | numfmt --to=iec-i --suffix=B --delimiter=$'\t'
目錄名稱中的換行符呈現為
\n
,反斜杠(至少在目前語言環境中解碼為\\
.如果在多個目錄中找到文件,則將其與在其中找到的第一個目錄進行計數(順序不確定)。
它假定環境中沒有
POSIXLY_CORRECT
變數(如果有,則設置PROCINFO["sorted_in"]
無效,gawk
因此不會對列表進行排序)。如果你不能保證³,你總是可以從gawk
(env -u POSIXLY_CORRECT gawk ...
假設 GNUenv
或兼容的;或(unset -v POSIXLT_CORRECT; gawk ...)
)開始。您的方法還有一些其他問題:
- 如果沒有
LC_ALL=C
,GNUfind
不會報告其名稱在區域設置中不構成有效字元的文件,因此您可能會錯過一些文件。- 嵌入
{}
程式碼中sh
構成任意程式碼注入漏洞。例如一個名為$(reboot).py
. 你永遠不應該這樣做,文件的路徑應該作為額外的參數傳遞,並使用位置參數在程式碼中引用。echo
不能用於顯示任意數據(尤其是在-e
這裡沒有意義的數據)。改為使用printf
。xargs -r0 du -sch
如果文件列表很大,du
可以多次呼叫with ,在這種情況下,最後一行將僅包括最後一次執行的總數。¹
%b
以 512 字節為單位報告磁碟使用情況。512 字節是磁碟分配的最小粒度,因為這是傳統扇區的大小。還有%k
which isint(%b / 2)
,但這會在具有 512 字節塊的文件系統上給出不正確的結果(文件系統塊通常是 2 的冪,並且至少 512 字節大)² 使用
LC_ALL=C
for gawk 也會提高效率,但可能會使用 BIG5 或 GB18030 字元集(並且文件名也在該字元集中編碼)在語言環境中破壞輸出,因為反斜杠的編碼也可以在編碼中找到那裡的一些其他角色。³ 請注意,如果您的
sh
isbash
,在腳本POSIXLY_CORRECT
中設置為,並且如果以or開頭,則會將其導出到環境中,因此該變數也可能無意中潛入。y``sh``sh``-a``-o allexport
通過在一個數組中收集所有目錄總和並在最後列印它們(使用 GNU awk)來簡化來自@HaukeLaging 的解決方案。此外,只
numfmt
需要一次呼叫(最後)。#!/bin/sh find . -type f -iname '*.py' -printf '%s %h\0' | awk 'BEGIN { RS="\0"; }; { gsub(/\\/,"&&"); gsub(/\n/,"\\n"); size=$1; sub("[^ ]* ",""); dirsize[$0]+=size } END { PROCINFO["sorted_in"] = "@val_num_desc"; i=0; for ( dir in dirsize ) { if(++i<=50) { print dirsize[dir], dir; }else{ exit } } } ' | numfmt --to=iec-i --suffix=B
這會生成 py 文件的累積表觀大小(而不是它們的磁碟使用情況),並避免對目錄的子目錄中的文件求和。