Я пытаюсь держать каталог, полный файлов журнала, управляемым. Ночью я хочу удалить все, кроме 10 самых последних. Как я могу сделать это в одной команде?
Я пытаюсь держать каталог, полный файлов журнала, управляемым. Ночью я хочу удалить все, кроме 10 самых последних. Как я могу сделать это в одной команде?
Ответы:
Для портативного и надежного решения, попробуйте это:
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 выше (но все еще страдает от новых строк в именах файлов).
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".
Код, который вы хотите включить в свой скрипт
rm -f $(ls -1t /path/to/your/logs/ | tail -n +11)
Опция -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 дней, вы можете сделать следующее:
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
-mtime 10 -delete
удаляет только файлы, измененные ровно 10 дней назад. Старые файлы не будут удалены. -mtime +11 -delete
удалит файлы, которые были изменены 11 или более дней назад, оставив последние 10 дней файлов журнала.
%p
вместо %P
необходимо для того, printf
чтобы файлы получили полный путь. В противном случае rm -rf
не работает.
Я немного изменил подход Исаака .
Теперь он работает с желаемым путем:
ls -d -1tr /path/to/folder/* | head -n -10 | xargs -d '\n' rm -f
От сюда :
#! /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 может быть лучше, я просто хочу исправить неправильные ответы, связанные с оболочкой.
Не уверен, что это кому-нибудь поможет, но чтобы избежать потенциальных проблем со странными символами, которые я использовал 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 днях. Ответ Эспо был самым коротким и чистым и, вероятно, самым неожиданным.
Ошибка с пробелами и когда файлы не должны быть удалены, просто решаются стандартным способом:
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"
Основано на ответах Исаака Фримена, но работает с любым каталогом:
ls -1tr $PATH_TO_DELETE/* | head -n -10 | xargs -d '\n' rm -f --
Вы также можете установить префикс, например, $PATH_TO_DELETE/testFi*
Если вы используете *
после того, как PATH
ls
будет выводить абсолютные имена файлов, по крайней мере, в "моем" Bash :)