Использование на диске Linux файлов, соответствующих выражению

У меня есть подключенный жесткий диск с несколькими пользователями, например:

/HDD1/user1
/HDD1/user2
/HDD1/user3

Я хотел бы заглянуть в папку каждого пользователя, найти все файлы, соответствующие выражению (скажем, "*.txt"), а затем суммировать пространство, используемое всеми этими файлами, и сообщить об этом для каждого пользователя:

user1: x bytes
user2: y bytes
user3: z bytes

Я нашел каталоги всех файлов с:

find /HDD1/ -name "*.txt" | rev | cut -d"/" -f2- | rev | uniq > txtfiles.dat

Я думал, что буду использовать цикл для прохождения каждой строки в txtfiles.dat, вычисляя использование диска в каждой папке, но это кажется очень громоздким. Есть ли более аккуратный способ сделать это? Что-то вроде du, которое просматривает папку каждого пользователя, но считает только те файлы, которые соответствуют выражению?


person James    schedule 15.06.2015    source источник
comment
Подсказка: du -ac $(find . -name '*.txt')   -  person Paul    schedule 15.06.2015


Ответы (5)


du берет список и изменяет размер отдельных файлов, если указана опция -a, и выводит итог с помощью опции -c.

В оболочке bash $(cmd) является результатом запуска cmd.

Итак, собрав все это вместе, чтобы получить размеры всех файлов .txt, можно запустить:

du -ac $(find . -name '*.txt')

person Paul    schedule 15.06.2015

find /HDD1/ -name "*.txt" -print0 | du -ch --files0-from -

Объяснение: команда find обеспечивает фильтрацию набора файлов, а указание -print0 завершает вывод NUL.

Выход find передается на du. Указание --files0-from - указывает, что пути к файлам считываются в завершающемся NUL потоке из STDIN. Наконец, -ch инструктирует du добавить итоговую строку в удобочитаемой форме.

Если все, что вам нужно, это общая сумма, вы можете передать результат tail -1:

find /HDD1/ -name "*.txt" -print0 | du -ch --files0-from - | tail -1
person voithos    schedule 15.06.2015
comment
Это будет общее количество в каждом подкаталоге HDD1 или общее количество? - person James; 15.06.2015
comment
@James: Это даст общее количество на HDD1, но вы можете легко обернуть его в цикл for, чтобы вычислить сумму для каждого каталога. - person voithos; 15.06.2015

Команда find (в большинстве систем) может вывести размер файла в байтах. Затем вы можете просто суммировать это, например, с помощью awk.

for userdir in /HDD1/*
do find "$userdir" -type f -name '*.txt' -printf '%s\n' |
   awk -v u=$(basename "$userdir") '
        {tot+=$1}
        END{print u ": " tot " bytes"}
   '
done

Это обеспечивает формат вывода, который вы запрашивали (user1: x bytes). Если число байтов велико, разделите его на 1000 или 1024, чтобы получить килобайты или кибибайты и т. д. (в awk напечатайте tot/1000 вместо tot).

person meuh    schedule 15.06.2015

Вы можете просмотреть каждого пользователя и суммировать размеры следующим образом:

for username in `ls /HDD1/`; do
  find $username -iname *.txt -printf '%s\n' | awk '{s+=$1}END{print s}' -
done

Внешний цикл предназначен для пользователей, затем find выполняет поиск файлов и распечатывает их размеры, и, наконец, awk суммирует все размеры и распечатывает результат.

person fahhem    schedule 15.06.2015
comment
Это должно быть в bash? В данный момент я использую zsh (но при необходимости могу легко изменить). Я не понимаю, где в коде появляется переменная username. - person James; 15.06.2015
comment
вы забыли найти каталог arg. также -printf нуждается в новой строке в формате. - person meuh; 15.06.2015
comment
Я не знаю zsh, но единственная вещь в bash - это цикл for, поэтому вы можете легко преобразовать его. Я забыл указать $username в поиске, спасибо @meuh за это! Тем не менее, -printf не нуждается в новой строке, так как он входит в awk, а awk просто нуждается в пробелах. - person fahhem; 16.06.2015
comment
да, awk работает с пробелами, но вы используете $1 и в каждой строке есть только 1 поле 1, поэтому вам нужно иметь каждое число в новой строке или суммировать по всем полям одной очень длинной строки (используя $i для i=1...NF). - person meuh; 17.06.2015

Я собрал кусочки из всех ваших ответов в это:

for n in /HDD1/*
  do
  uname=$(echo $n | cut -d'/' -f4)
  space=$(find /HDD1/$uname -name "*.txt" -print0 | du -ch --files0- from - | tail -1)
  echo $uname: $space
done

Который, кажется, работает нормально. Спасибо за все Ваши ответы.

person James    schedule 15.06.2015