Разбить каталог по расширению файла и получить общий размер и количество для каждого

Ваш пример:

chown :group file

не работает (может работать на некоторых платформах, но не является стандартным ). Обычно при этом меняется владелец файла.

Также обратите внимание, что метод двоеточия в chown был добавлен ок. 25 лет назад, а chgrpнамного старше.

3
15.01.2021, 01:49
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
0
18.03.2021, 23:15

Базовый код

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 {} +запускает duстолько раз, сколько необходимо (, чтобы избежать argument list too long), а файлы с жесткими ссылками могут передаваться или не передаваться одному и тому же du.Вы можете принудительно подсчитывать каждую жесткую ссылку, используяdu -l(не -портативную опцию в GNUdu)или переносимую, запустив одну duдля каждого файла:find … -exec du {} \;. Чтобы надежно подсчитать жесткие ссылки только один раз, вам нужен другой подход (один экземпляр GNU 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в одной функции. Еще отдельные awkпозволяют нам поставить, например. sort …между (в одной функции оболочки или в канале между функциями ). Хотя какая-то сортировка может быть реализована вawk(или, по крайней мере, в GNU awk), нет смысла изобретать велосипед. IMO сохранение вывода из первого 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')"

0
18.03.2021, 23:15

Теги

Похожие вопросы