Я намагаюся тримати керований каталог файлів журналів. Щовечора я хочу видалити всі, крім 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'th. Я зареєструвався ще раз, і команда повинна безумовно видаляти лише елементи, старіші за 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 {} \;
Мені потрібно було елегантне рішення для зайнятого ящика (маршрутизатора), всі рішення xargs або масиву були для мене марними - такої команди там немає. Знайти і 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'
. Не те, щоб ми в реальному житті створювали файли з такими іменами!
Sidenote. Якби ми хотіли передати змінну до 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
заповіту, виведете абсолютні імена файлів, принаймні в "моєму" баші :)