Я пытаюсь держать каталог, полный файлов журнала, управляемым. Ночью я хочу удалить все, кроме 10 самых последних. Как я могу сделать это в одной команде?
9 ответов
Для портативного и надежного решения, попробуйте это:
ls -1tr | head -n -10 | xargs -d '\n' rm -f --
Синтаксис tail -n -10
в одном из других ответов, кажется, не работает везде (т.е. не в моих системах RHEL5).
А использование $()
или ``
в командной строке rm
рискует
- разделение имен файлов пробелами и
- превышение максимального ограничения символов командной строки.
xargs
исправляет обе эти проблемы, потому что он автоматически определит, сколько аргументов он может передать в пределах ограничения на число символов, а с помощью -d '\n'
он будет разбит только на границе строки ввода. Технически это все еще может вызвать проблемы для имен файлов с новой строкой в них, но это гораздо реже, чем имена файлов с пробелами, и единственный способ обойти новые строки будет намного сложнее, вероятно, с использованием по крайней мере awk, если не perl.
Если у вас нет xargs (старые системы AIX, может быть?) Вы можете сделать это циклом:
ls -1tr | head -n -10 | while IFS= read -r f; do
rm -f "$f"
done
Это будет немного медленнее, потому что он порождает отдельный rm
для каждого файла, но все же избегает предостережений 1 и 2 выше (но все еще страдает от новых строк в именах файлов).
Код, который вы хотите включить в ваш скрипт:
rm -f $(ls -1t /path/to/your/logs/ | tail -n +11)
Параметр -1 (числовой) печатает каждый файл в одной строке, чтобы быть в безопасности. Опция -f для rm говорит ему игнорировать несуществующие файлы, когда ls ничего не возвращает.
Видимо, разбор ls
- это зло.
Если каждый файл создается ежедневно и вы хотите сохранить файлы, созданные в течение последних 10 дней, вы можете сделать следующее:
find /path/to/files -mtime 10 -delete
Или, если каждый файл создан произвольно:
find /path/to/files -maxdepth 1 -type f -printf '%Ts\t%P\n' | sort -n | head -n -10 | cut -f 2- | xargs rm -rf
Такой инструмент, как logrotate, сделает это за вас. Это делает управление журналами намного проще. Вы также можете включить дополнительные процедуры очистки, такие как ореол.
Я немного изменил подход Исаака .
Теперь он работает с желаемым путем:
ls -d -1tr /path/to/folder/* | head -n -10 | xargs -d '\n' rm -f
Не уверен, что это кому-нибудь поможет, но чтобы избежать потенциальных проблем со странными символами, я просто использовал ls
для выполнения сортировки, но затем использую inode
для удаления.
Для текущего каталога, он сохраняет последние 10 файлов в зависимости от времени модификации.
ls -1tri | awk '{print $1}' | head -n -10 | xargs -n1 -t -Iinode find . -inum inode -exec rm -i {} \;
#! /bin/sh
# keepnewest
#
# Simple directory trimming tool to handle housekeeping
# Scans a directory and deletes all but the N newest files
#
# Usage: cleanup <dir> <number of files to keep>
#
# v 1.0 Piers Goodhew 1/mar/2007. No rights retained.
if [ $# -ne 2 ]; then
echo 1>&2 "Usage: $0 <dir> <number of files to keep>"
exit 1
fi
cd $1
files_in_dir=`ls | wc -l`
files_to_delete=`expr $files_in_dir - $2`
if [ $files_to_delete -gt 0 ]; then
ls -t | tail -n $files_to_delete | xargs rm
if [ $? -ne 0 ]; then
echo "An error ocurred deleting the files"
exit 1
else
echo "$files_to_delete file(s) deleted."
fi
else
echo "nothing to delete!"
fi
В Bash вы можете попробовать:
ls -1t | (i=0; while read f; do
if [ $i -lt 10 ]; then
((i++))
continue
else
rm -f "$f"
fi
done)
Это пропускает 10 новейших файлов, удаляет остальные. logrotate может быть лучше, я просто хочу исправить неправильные ответы, связанные с оболочкой.
Мне нужно было элегантное решение для busybox (маршрутизатора), все решения для xargs или array были для меня бесполезны - такой команды там не было. find и mtime не правильный ответ, так как речь идет о 10 пунктах, а не обязательно 10 днях. Ответ Эспо был самым коротким и чистым и, вероятно, самым неожиданным.
Ошибка с пробелами и когда файлы не должны быть удалены, просто решаются стандартным способом:
rm "$(ls -td *.tar | awk 'NR>7')" 2>&-
Немного больше образовательной версии: мы можем сделать все это, если будем использовать awk по-другому. Обычно я использую этот метод для передачи (возврата) переменных из awk в sh. Поскольку мы все время читаем, что не может быть сделано, я позволю себе не согласиться: вот метод.
Пример для файлов .tar без проблем с пробелами в имени файла. Чтобы проверить, замените "rm" на "ls".
eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}')
Объяснение:
ls -td *.tar
перечисляет все файлы .tar, отсортированные по времени. Чтобы применить ко всем файлам в текущей папке, удалите часть "d * .tar".
awk 'NR>7...
пропускает первые 7 строк
print "rm \"" $0 "\""
создает строку: rm "имя файла"
eval
выполняет его
Поскольку мы используем rm
, я бы не использовал вышеуказанную команду в скрипте! Более мудрое использование:
(cd /FolderToDeleteWithin && eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}'))
В случае использования команды ls -t
не будет никакого вреда для таких глупых примеров, как: touch 'foo " bar'
и touch 'hello * world'
. Не то чтобы мы когда-либо создавали файлы с такими именами в реальной жизни!
Примечание. Если бы мы хотели передать переменную sh таким образом, мы бы просто изменили вывод:
print "VarName="$1
установить переменную VarName
в значение $1
. Несколько переменных могут быть созданы за один раз. Это VarName
становится обычной переменной sh и впоследствии может быть использовано в скрипте или оболочке. Итак, чтобы создать переменные с помощью awk и вернуть их обратно в оболочку:
eval $(ls -td *.tar | awk 'NR>7 { print "VarName="$1 }'); echo "$VarName"