Проблемы с существующими ответами:
- невозможность обрабатывать имена файлов со встроенными пробелами или переводами строки.
- В случае решений, которые вызывают
rm
непосредственно при подстановке команды без кавычек (rm `...`
), существует дополнительный риск непреднамеренного сглаживания.
- неспособность провести различие между файлами и каталогами (т. Е. Если бы каталоги оказались в числе 5 самых последних измененных элементов файловой системы, вы фактически сохранили бы меньше , чем 5 файлов, и применение
rm
к каталогам не удастся).
ответ wnoise решает эти проблемы, но решение является GNU -конкретным (и довольно сложным).
Вот прагматичное POSIX-совместимое решение , которое поставляется только с одним предупреждением : оно не может обрабатывать имена файлов со встроенными символами новой строки - но я не рассматриваю это реальная проблема для большинства людей.
Для справки, вот объяснение того, почему вообще не очень хорошая идея анализировать ls
вывод: http://mywiki.wooledge.org/ParsingLs
ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}
Выше неэффективно , потому что xargs
должен вызывать rm
один раз для каждого имени файла.
xargs
вашей платформы может позволить вам решить эту проблему:
Если у вас есть GNU xargs
, используйте -d '\n'
, что заставляет xargs
считать каждую входную строку отдельным аргументом, но при этом передает столько аргументов, сколько поместится на командная строка сразу :
ls -tp | grep -v '/$' | tail -n +6 | xargs -d '\n' -r rm --
-r
(--no-run-if-empty
) гарантирует, что rm
не вызывается, если нет ввода.
Если у вас есть BSD xargs
(включая OS X ), вы можете использовать -0
для обработки NUL
-разделенного ввода, после первого перевода символов новой строки в NUL
(0x0
) символов, который также передает (обычно) все имена файлов сразу (также будет работать с GNU xargs
):
ls -tp | grep -v '/$' | tail -n +6 | tr '\n' '\0' | xargs -0 rm --
Пояснение:
ls -tp
печатает имена элементов файловой системы, отсортированные по тому, как недавно они были изменены, в порядке убывания (сначала самые последние измененные элементы) (-t
), с каталогами, напечатанными с конечным символом /
, чтобы пометить их как таковые (-p
).
grep -v '/$'
затем отсеивает каталоги из результирующего списка, пропуская (-v
) строки, которые имеют конечный /
(/$
).
- Предупреждение : Поскольку символическая ссылка , которая указывает на каталог , технически сама по себе не является каталогом, такие символические ссылки не будут исключены.
tail -n +6
пропускает первые 5 записей в списке, по сути возвращая все , но 5 самых последних измененных файлов, если таковые имеются.
Обратите внимание, что для исключения N
файлов N+1
необходимо передать в tail -n +
.
xargs -I {} rm -- {}
(и его варианты) затем вызывает rm
для всех этих файлов; если совпадений нет вообще, xargs
ничего не сделает.
xargs -I {} rm -- {}
определяет заполнитель {}
, который представляет каждую строку ввода в целом , поэтому rm
затем вызывается один раз для каждой строки ввода, но с именами файлов со встроенными пробелами, которые обрабатываются правильно.
--
во всех случаях гарантирует, что любые имена файлов, начинающиеся с -
, не будут приняты за options rm
.
A вариант для исходной задачи, в случае необходимости обработки соответствующих файлов по отдельности или , собранных в массиве оболочки :
# One by one, in a shell loop (POSIX-compliant):
ls -tp | grep -v '/$' | tail -n +6 | while IFS= read -r f; do echo "$f"; done
# One by one, but using a Bash process substitution (<(...),
# so that the variables inside the `while` loop remain in scope:
while IFS= read -r f; do echo "$f"; done < <(ls -tp | grep -v '/$' | tail -n +6)
# Collecting the matches in a Bash *array*:
IFS=$'\n' read -d '' -ra files < <(ls -tp | grep -v '/$' | tail -n +6)
printf '%s\n' "${files[@]}" # print array elements