обоснование
Это моя попытка создать команду, которая будет работать с любыми каталогами и именами файлов. В общем пути в 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 нет ввода.