ファイルを再帰的にリストし、ユーザーごとにグループ化してサイズを合計する

ファイルを再帰的にリストし、ユーザーごとにグループ化してサイズを合計する

Linux サーバー上の各ユーザーが占有するサイズ (データ フォルダー内のすべてのファイルが再帰的に占有されている) を確認する必要があります。

以下のコードを使用すると、すべてのファイルとそのユーザーを取得できますが、その後それらをグループ化して合計する方法がわかりません。

#> find . -type f -ls | sort -k5

この問題を解決するアイデアを持っている人はいますか?

答え1

GNUの場合find:

find . -printf '%D+%i:%u:%b\n' | awk -F: '
  !seen[$1]++ {du[$2] += $3}
  END {for (user in du) printf "%20s: %lu\n", user, du[user] * 512}'

これにより、各ユーザーのディスク使用量がバイト単位で報告されます。 と同様にdu、ハードリンクを複数回カウントしないように注意します。

find各ファイルのデバイス + inode 番号 ( %D+%i) (同じファイルへのハードリンクの場合は同一になります)、ユーザー名、および 512 バイト単位のディスク使用量を出力します。

:はフィールド区切り文字として使用されます。これは、ユーザー名には通常含まれていないためであり、ユーザー データベース内のフィールド区切り文字であるためです (または/etc/passwdの出力などgetent passwd)。

そのリストは に送られawk、そこで最初のフィールドがまだ されていない各ファイルについて、duユーザー名 (2 番目のフィールド) でインデックス付けされた連想配列が更新されます。$2seen

ではEND、連想配列の要素をループして、各ユーザーの累積ディスク使用量を報告します(ブロック情報をバイト単位で取得します。

答え2

これでうまくいくはずです。少し遅く、/etc/passwd 内のすべてのユーザーを使用しますが、これは簡単に変更できます。また、どのようなユーザーがいるかわかりません (lastlogおそらくうまくいくと思います)

これは現在の作業ディレクトリを使用することに注意してください( を参照find .)。

ワンライナー:

for user in $(cut -d: -f1 < /etc/passwd); do echo -en "$user has:\t"; find . -user $user -type f -print0 | du --files0-from=- --total -sh | tail -n1 ; done

以下は同じですが、もう少し詳細です。

# Read all lines in /etc/password, use ":" as field separator and print first field
for user in $(cut -d: -f1 < /etc/passwd); do
  # Print username, -e to allow \t, -n to skip newline
  echo -en "$user is using:\t"
  # Find all files owned by $user, print found files to stdout and terminate
  # with a null character (thus circumventing the long file list problem).
  # let `du` read from null terminated stdin usint --files0-from=-, make a total,
  # make a summary and make it human readable, then only print the last line
  # containing the total
  find . -user "$user" -type f -print0 | du --files0-from=- --total -sh | tail -n1
done

答え3

各ユーザーが所有するファイルが占めるドライブ容量を計算する

このbashシェルスクリプトは

  • find所有者を見つけるために
  • 各所有者に属するすべてのファイルを検索するコマンドライン forのループfind
    • ファイル名をパイプしてdu
  • du結果が読みやすくなるように後処理されます。

多数のファイルがあるパーティションでテストすると、シェルスクリプトはかなり高速です。

#!/bin/bash

# store owners in variable

user=$(whoami)
if [ "$user" != "root" ]
then
 echo "Run with elevated permissions (as root or with sudo)"
 exit
elif ! test -d "$1"
then
 echo "Enter a target directory as parameter"
 exit
fi

owners=$(find "$1" -printf "%u\n"|sort | uniq)
#echo "$owners"

leng=0
for i in $owners
do
 if [ ${#i} -gt $leng ]
 then
  leng=${#i}
 fi
done

#echo "$leng"
skip=$((leng - 4))
spc=$(for ((i=0;i<skip;i++)); do printf " "; done)


printf "User $spc Size\n---- $spc ----\n"

for i in $owners
do
 skip=$((leng - ${#i}))
 spc=$(for ((i=0;i<skip;i++)); do printf " "; done)
 printf "$i $spc "
 find "$1" -type f -user "$i" -print0 | du --files0-from=- --total -sh |
  tail -n1 | cut -f 1
done

デモ例

名前を名乗るdisk-usage-by-owner

ubuntu@ubuntu:~$ ./disk-usage-by-owner
Run with elevated permissions (as root or with sudo)
ubuntu@ubuntu:~$ sudo ./disk-usage-by-owner
Enter a target directory as parameter

持続的なライブドライブでの対話

ubuntu@ubuntu:~/bin$ sudo ./disk-usage-by-owner /cdrom
User  Size
----  ----
root  1.9G
ubuntu@ubuntu:~/bin$ sudo ./disk-usage-by-owner /home
User    Size
----    ----
root    0
ubuntu  1.9G
ubuntu@ubuntu:~/bin$ sudo ./disk-usage-by-owner /media/ubuntu/casper-rw
User              Size
----              ----
_apt              0
colord            44K
gdm               288K
man               628K
root              1007M
syslog            127M
systemd-timesync  0
ubuntu            1.9G

ハードリンクは1回だけカウントされます

$ sudo find . -user root -ls
56492055 1024 -rw-r--r-- 3 root root 1048576 jan 16 23:41 ./owned\ by\ root\ hard-linked
56492055 1024 -rw-r--r-- 3 root root 1048576 jan 16 23:41 ./owned\ by\ root
56492055 1024 -rw-r--r-- 3 root root 1048576 jan 16 23:41 ./sub/owned\ by\ root

$ sudo ./disk-usage-by-owner .
User     Size
----     ----
root     1,0M
sudodus  32K

$ du .
4   ./sub
1064    .

答え4

find、stat、awk の正しいバージョンがあれば、少し速くなるかもしれません。

find . -type f -exec stat -c "%U %s" {} \; | awk '{sum[$1]+=$2} END {for (u in sum) {printf("%s: %d\n", u, sum[u])}}'

これは、find(1) コマンドで見つかったすべてのファイルに対して stat(1) コマンドを実行します。stat はユーザー名とファイルのサイズを出力します。これは次に awk に渡されます。awk コマンドは、指定されたユーザーのすべてのファイルのサイズを合計します。すべてのファイルが処理されると、合計リストにあるすべてのユーザーと、そのユーザーのすべてのファイルの合計サイズが出力されます。

関連情報