обоснование
Это моя попытка создать команду, которая будет работать с любыми каталогами и именами файлов. В общем пути в Linux (и имена в файловых системах) могут содержать любые символы, кроме null (0x00
) и /
. Проблемные персонажи могут быть
"(пробел), любой другой белый символ, '
, "
, перевод строки, другие непечатные символы. Поэтому важно:
- отказаться от инструментов, которые заменяют одни символы другими (например, многие реализации
ls
будут печатать ?
для непечатаемых документов);
- передать все имена как строки с нулевым символом в конце (выбрать инструменты, которые могут их анализировать);
- цитата правильно.
Я был вдохновлен обсуждением этого другого ответа.
Актуальные команды
Тестирование версии, это будет только ls
файлы , которые будут удалены:
find -type d -exec sh -c 'find "$0" -maxdepth 1 -mindepth 1 -type f -exec stat --printf "%s %n\0" \{\} + | sort -znr | tail -zn +2' {} \; | cut -zf 2- -d " " | xargs -0r ls -l
Да, я использую ls
здесь, несмотря на то, что я только что сказал. Это потому, что вывод ls
не анализируется дальше. Я использую его только для отображения результата. Если у вас есть каталоги или файлы с проблемными символами в именах, вы будете наблюдать поведение ls
которое должно убедить вас никогда не анализировать ls
(если вы не уверены, что с ним абсолютно безопасно). Тем не менее, проблемные имена пройдут весь путь до ls
и в этом все дело.
Разберитесь с тестовой версией (некоторые объяснения см. Ниже) и попробуйте, прежде чем позволить рабочей версии (чуть ниже) удалить свои файлы. Помните, я просто случайный парень в Интернете.
Рабочая версия, она удалит ваши файлы:
find -type d -exec sh -c 'find "$0" -maxdepth 1 -mindepth 1 -type f -exec stat --printf "%s %n\0" \{\} + | sort -znr | tail -zn +2' {} \; | cut -zf 2- -d " " | xargs -0r rm
объяснение
Вот тестовая версия, разделенная на несколько строк (хотя это все еще одна строка для bash
; обратите внимание, я использую этот трюк для встроенных комментариев):
find -type d -exec `# Find all directories under (and including) the current one.` \
sh -c ' `# In every directory separately...` \
find "$0" -maxdepth 1 -mindepth 1 -type f -exec `# ...find all files,...` \
stat --printf "%s %n\0" \{\} + | # ...get their sizes and names,...
sort -znr | # ...sort by size...
tail -zn +2' `# ...and discard the "biggest" entry.` \
{} \
\; | # (All the directories have been processed).
cut -zf 2- -d " " | # Then extract filenames...
xargs -0r ls -l # ...and ls them (rm in the working version).
Используемая техника, преодоленные препятствия:
- Инструментам, которые разбирают строки, предписано работать со строками с нулевым символом в конце:
stat --printf "…\0"
;
sort -z
, tail -z
, cut -z
;
xargs -0 …
;
find -print0
(не требуется в этом примере, но очень распространено в общем, поэтому я все равно упоминаю об этом).
sh -c '…'
- это способ использования каналов внутри find -exec
.
find -type d -exec sh -c 'find "{}" …
остановится для имени каталога, содержащего "
; find -type d -exec sh -c 'find "$0" … ' {} \;
работает нормально.
{}
во внутреннем операторе find
экранированы (\{\}
), чтобы предотвратить их замену внешним find
.
cut
мог сразу следовать за tail
, он запускал бы один cut
на каталог. Размещение его вне внешней find
заставляет один cut
делать все разрезание одновременно.
- Параметр
-r
для xargs
запрещает запуск ls
(rm
в рабочей версии), если в xargs
нет ввода.