Как удалить все, кроме 10 новейших файлов в Linux?
49
Я пытаюсь держать каталог, полный файлов журнала, управляемым. Ночью я хочу удалить все, кроме 10 самых последних. Как я могу сделать это в одной команде?
@Michael Как мой может быть дубликатом этого, если моя дискуссия была создана в первую очередь?
dev4life
@ovaherenow "твой"? Я не вижу твоего имени ни в одном вопросе, поэтому не уверен, что ты имеешь в виду. Мой комментарий даже не указывает, что является дубликатом другого. Но это не имеет значения - это просто комментарий со ссылкой. Его можно добавить либо к вопросу, либо к обоим вопросам. Это зависит от кого-то еще - администратора - чтобы пометить один или другой как дубликат. Никакой гнусной ловкости не было и не должно быть. Более важным, чем вопрос был первым, является вопрос, какой из них имеет лучший ответ - опять же, ссылка облегчает эту дискуссию, но не определяет ответ.
Майкл
@ Майкл вау. Это действительно странно. Эта тема была открыта мной, но это явно не мое имя пользователя. Но я получаю уведомление об этой теме.
dev4life
Ответы:
58
Для портативного и надежного решения, попробуйте это:
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 выше (но все еще страдает от новых строк в именах файлов).
@HaukeLaging: со head(1)страницы руководства :-n, --lines=[-]K print the first K lines instead of the first 10; with the leading '-', print all but the last K lines of each file
Исаак Фриман
На Солярисе 10 headи xargsвыдают ошибки. Правильные варианты есть head -n 10и xargs rm -f. Полная командаls -1tr ./ | head -n 10 | xargs rm -rf
Дмитрий Сандалов
1
Обратите внимание, что ls -1trномер ОДИН, а не буква "L".
пользователь shonky linux
1
Следует также отметить, что переводы строки - это редкие, но допустимые символы в именах файлов ext. Это означает, что если у вас есть файлы «тот» и «тот \ nthing», если этот скрипт ударит «тот \ nthing», он удалит «тот», ошибку, когда он не смог удалить «вещь», и оставит «что \ nthing» (предполагаемая цель) не затрагивается.
Synthead
1
@IsaacFreeman Я удалил свой плохой совет, ура.
Уолф
20
Код, который вы хотите включить в свой скрипт
rm -f $(ls -1t /path/to/your/logs/ | tail -n +11)
Опция -1(числовая) печатает каждый файл в одной строке, чтобы быть в безопасности. -fВариант rmговорит , что игнорировать файлы несуществующие для того, когда lsвозвращает ничего.
Оригинальный постер, здесь. Я попробовал эту команду, но она не работает - я получаю сообщение об ошибке «rm: отсутствующий операнд»
user75814
2
Вы использовали правильный путь? Очевидно, /path/to/your/logsэто просто сделано для вас, чтобы ввести правильный путь. Для того, чтобы найти ошибку выполнения частей ls -t /path/...и в ls -t /path/... | tail -n +11одиночку , и проверить , если результат является правильным.
Даниэль Бёмер
1
@HaukeLaging Я совершенно уверен, что вы что-то не так поняли. Следите +за числом. Он tailпечатает не последние n элементов, а все элементы, начиная с n-го. Я зарегистрировался снова, и команда должна обязательно удалить только элементы старше 10 самых последних файлов при любых обстоятельствах.
Даниэль Бёмер
@ Halo Действительно, извини.
Хауке Лагинг
2
Я думаю, что ваш ответ очень опасен, lsпечатает имя файла, а не путь, поэтому вы rm -fбудете пытаться удалить файлы в текущем каталоге
-mtime 10 -deleteудаляет только файлы, измененные ровно 10 дней назад. Старые файлы не будут удалены. -mtime +11 -deleteудалит файлы, которые были изменены 11 или более дней назад, оставив последние 10 дней файлов журнала.
Маркус
1
Я думаю, что использование %pвместо %Pнеобходимо для того, printfчтобы файлы получили полный путь. В противном случае rm -rfне работает.
Халопаба
9
Такой инструмент, как logrotate, сделает это за вас. Это делает управление журналами намного проще. Вы также можете включить дополнительные процедуры очистки, такие как предложенный гало.
#! /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
Спасибо за предоставленный ответ. Хотя предоставление кода, подобного этому, полезно, оно также помогает предоставить некоторую дополнительную информацию о том, как его можно использовать или что делает код. Вы можете использовать кнопку редактирования , чтобы в будущем улучшить свой пост.
nhinkle
1
В Bash вы можете попробовать:
ls -1t | (i=0; while read f; do
if [ $i -lt 10 ]; then
((i++))
continue
else
rm -f "$f"
fi
done)
Это пропускает 10 новейших файлов, удаляет остальные. logrotate может быть лучше, я просто хочу исправить неправильные ответы, связанные с оболочкой.
Не уверен, что это кому-нибудь поможет, но чтобы избежать потенциальных проблем со странными символами, которые я использовал lsдля сортировки, а затем использовать файлы inodeдля удаления.
Для текущего каталога это сохраняет последние 10 файлов в зависимости от времени модификации.
Мне нужно было элегантное решение для busybox (роутера), все решения xargs или array были для меня бесполезны - такой команды там не было. find и mtime не правильный ответ, так как речь идет о 10 пунктах и не обязательно 10 днях. Ответ Эспо был самым коротким и чистым и, вероятно, самым неожиданным.
Ошибка с пробелами и когда файлы не должны быть удалены, просто решаются стандартным способом:
rm "$(ls -td *.tar | awk 'NR>7')" 2>&-
Немного больше образовательной версии: мы можем сделать все это, если будем использовать awk по-другому. Обычно я использую этот метод для передачи (возврата) переменных из awk в sh. Поскольку мы все время читаем, что не может быть сделано, я позволю себе не согласиться: вот метод.
Пример для файлов .tar без проблем с пробелами в имени файла. Чтобы проверить, замените «rm» на «ls».
В случае использования ls -tкоманда не нанесет никакого вреда таким глупым примерам, как: touch 'foo " bar'и touch 'hello * world'. Не то чтобы мы когда-либо создавали файлы с такими именами в реальной жизни!
Примечание. Если бы мы хотели передать переменную sh таким образом, мы бы просто изменили вывод:
print "VarName="$1
установить переменную VarNameв значение $1. Несколько переменных могут быть созданы за один раз. Это VarNameстановится нормальной переменной sh и впоследствии может быть использовано в скрипте или оболочке. Итак, чтобы создать переменные с помощью awk и вернуть их обратно в оболочку:
Ответы:
Для портативного и надежного решения, попробуйте это:
tail -n -10
Синтаксис в одном из других ответов , кажется , не работать везде (то есть, не на моих системах RHEL5).И использование
$()
или``
в командной строкеrm
рискуетxargs
устраняет обе эти проблемы, потому что он автоматически выяснит, сколько аргументов он может передать в пределах ограничения на количество символов, и при этом-d '\n'
он будет разбиваться только на границе строки ввода. Технически это все еще может вызвать проблемы для имен файлов с новой строкой в них, но это гораздо реже, чем имена файлов с пробелами, и единственный способ обойти новые строки будет намного сложнее, вероятно, с использованием по крайней мере awk, если не perl.Если у вас нет xargs (возможно, старых систем AIX?), Вы можете сделать это циклом:
Это будет немного медленнее, потому что он порождает отдельное
rm
для каждого файла, но все равно будет избегать предостережений 1 и 2 выше (но все еще страдает от новых строк в именах файлов).источник
head(1)
страницы руководства :-n, --lines=[-]K print the first K lines instead of the first 10; with the leading '-', print all but the last K lines of each file
head
иxargs
выдают ошибки. Правильные варианты естьhead -n 10
иxargs rm -f
. Полная командаls -1tr ./ | head -n 10 | xargs rm -rf
ls -1tr
номер ОДИН, а не буква "L".Код, который вы хотите включить в свой скрипт
Опция
-1
(числовая) печатает каждый файл в одной строке, чтобы быть в безопасности.-f
Вариантrm
говорит , что игнорировать файлы несуществующие для того, когдаls
возвращает ничего.источник
/path/to/your/logs
это просто сделано для вас, чтобы ввести правильный путь. Для того, чтобы найти ошибку выполнения частейls -t /path/...
и вls -t /path/... | tail -n +11
одиночку , и проверить , если результат является правильным.+
за числом. Онtail
печатает не последние n элементов, а все элементы, начиная с n-го. Я зарегистрировался снова, и команда должна обязательно удалить только элементы старше 10 самых последних файлов при любых обстоятельствах.ls
печатает имя файла, а не путь, поэтому выrm -f
будете пытаться удалить файлы в текущем каталогеВидимо, разбор
ls
это зло .Если каждый файл создается ежедневно и вы хотите сохранить файлы, созданные в течение последних 10 дней, вы можете сделать следующее:
Или, если каждый файл создан произвольно:
источник
-mtime 10 -delete
удаляет только файлы, измененные ровно 10 дней назад. Старые файлы не будут удалены.-mtime +11 -delete
удалит файлы, которые были изменены 11 или более дней назад, оставив последние 10 дней файлов журнала.%p
вместо%P
необходимо для того,printf
чтобы файлы получили полный путь. В противном случаеrm -rf
не работает.Такой инструмент, как logrotate, сделает это за вас. Это делает управление журналами намного проще. Вы также можете включить дополнительные процедуры очистки, такие как предложенный гало.
источник
Я немного изменил подход Исаака .
Теперь он работает с желаемым путем:
источник
От сюда :
источник
В Bash вы можете попробовать:
Это пропускает 10 новейших файлов, удаляет остальные. logrotate может быть лучше, я просто хочу исправить неправильные ответы, связанные с оболочкой.
источник
Не уверен, что это кому-нибудь поможет, но чтобы избежать потенциальных проблем со странными символами, которые я использовал
ls
для сортировки, а затем использовать файлыinode
для удаления.Для текущего каталога это сохраняет последние 10 файлов в зависимости от времени модификации.
ls -1tri | awk '{print $1}' | head -n -10 | xargs -n1 -t -Iinode find . -inum inode -exec rm -i {} \;
источник
Мне нужно было элегантное решение для busybox (роутера), все решения xargs или array были для меня бесполезны - такой команды там не было. find и mtime не правильный ответ, так как речь идет о 10 пунктах и не обязательно 10 днях. Ответ Эспо был самым коротким и чистым и, вероятно, самым неожиданным.
Ошибка с пробелами и когда файлы не должны быть удалены, просто решаются стандартным способом:
Немного больше образовательной версии: мы можем сделать все это, если будем использовать awk по-другому. Обычно я использую этот метод для передачи (возврата) переменных из awk в sh. Поскольку мы все время читаем, что не может быть сделано, я позволю себе не согласиться: вот метод.
Пример для файлов .tar без проблем с пробелами в имени файла. Чтобы проверить, замените «rm» на «ls».
Объяснение:
ls -td *.tar
перечисляет все файлы .tar, отсортированные по времени. Чтобы применить ко всем файлам в текущей папке, удалите часть "d * .tar"awk 'NR>7...
пропускает первые 7 строкprint "rm \"" $0 "\""
конструирует строку: rm "имя файла"eval
выполняет этоПоскольку мы используем
rm
, я бы не использовал вышеуказанную команду в сценарии! Более разумное использование:В случае использования
ls -t
команда не нанесет никакого вреда таким глупым примерам, как:touch 'foo " bar'
иtouch 'hello * world'
. Не то чтобы мы когда-либо создавали файлы с такими именами в реальной жизни!Примечание. Если бы мы хотели передать переменную sh таким образом, мы бы просто изменили вывод:
установить переменную
VarName
в значение$1
. Несколько переменных могут быть созданы за один раз. ЭтоVarName
становится нормальной переменной sh и впоследствии может быть использовано в скрипте или оболочке. Итак, чтобы создать переменные с помощью awk и вернуть их обратно в оболочку:источник
Я согласен, что разбор ls это зло!
Убедитесь, что только последние 5 файлов хранятся в
/srv/backups
пути.источник
Основано на ответах Исаака Фримена, но работает с любым каталогом:
Вы также можете установить префикс, например,
$PATH_TO_DELETE/testFi*
Если вы используете
*
после того, какPATH
ls
будет выводить абсолютные имена файлов, по крайней мере, в "моем" Bash :)источник