Як видалити всі, крім 10 найновіших файлів у Linux?


49

Я намагаюся тримати керований каталог файлів журналів. Щовечора я хочу видалити всі, крім 10 останніх. Як я можу це зробити в одній команді?


3
подивіться на logrotate
knittl


@michael Як я можу бути дублікатом цього, якщо моє обговорення було створене першим?
dev4life

@ovaherenow "твій"? Я не бачу вашого імені в жодному питанні, тому я не впевнений, що ви маєте на увазі. Мій коментар навіть не вказує, що є дублікатом іншого. Але це не має значення - це лише коментар із посиланням. До нього можна додати або запитання, або обидва запитання. Від когось іншого - адміністратора - позначати той чи інший як дублікат. Жодна злісна хитрість не передбачалася і не повинна сприйматися. Більш важливим, ніж перше питання, є те, яке питання має найкращу відповідь - знову ж таки, посилання полегшує цю дискусію, вона не визначає відповіді.
michael

@michael вау Це справді дивно. Ця тема була відкрита мною, але це, очевидно, не моє ім’я користувача. Але я отримую сповіщення про цю тему.
dev4life

Відповіді:


58

Для портативного та надійного рішення спробуйте це:

ls -1tr | head -n -10 | xargs -d '\n' rm -f --

tail -n -10Синтаксис в одному з інших відповідей , здається , НЕ працювати скрізь (тобто, не на моїх системах RHEL5).

І використання $()або ``в командному рядку rmзапускає ризик

  1. розділення імен файлів на пробіли та
  2. перевищення максимальної межі символу командного рядка.

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
Ісаак Freeman

На Solaris 10 headі xargsдайте помилки. Правильні варіанти є head -n 10і xargs rm -f. Повна командаls -1tr ./ | head -n 10 | xargs rm -rf
Дмитро Сандалов

1
Зверніть увагу, що ls -1trце число ОДНЕ, а не літера "L".
приголомшливий користувач Linux

1
Слід також зазначити, що нові рядки є рідкісними, але дійсними символами у файлах ext. Це означає, що якщо у вас є файли "that" і "that \ nthing", якщо цей скрипт натискає "that \ nthing", він видалить "that", помилку, коли він не міг видалити "thing", і залишить "що \ 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'th. Я зареєструвався ще раз, і команда повинна безумовно видаляти лише елементи, старіші за 10 останніх файлів за будь-яких обставин.
Даніель Бьомер

@halo Дійсно, вибач.
Hauke ​​Laging

2
Я думаю, що ваша відповідь дуже небезпечна, lsдрукує ім'я файлу, а не шлях, тому ви rm -fспробуєте видалити файли з поточного каталогу
Jürgen Steinblock

14

Мабуть, розбір 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

5
-mtime 10 -deleteвидаляє лише файли, змінені рівно 10 днів тому. Старіші файли не будуть видалені. -mtime +11 -deleteвидалить файли, які були змінені 11 і більше днів тому, залишаючи останні 10 днів файлів журналу.
Маркус

1
Я думаю, що використання %pзамість цього %Pпотрібне для того, printfщоб отримані файли мали повний шлях. Інакше rm -rfне працює.
jalopaba

9

Такий інструмент, як логротат, робить це за вас. Це значно спрощує управління журналом. Ви також можете включити додаткові процедури очищення, як це було запропоновано ореолом.



1

Від сюди :

#! /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

1
Дякуємо, що надали цю відповідь. Надаючи такий код, як це корисно, він також допомагає надати додаткову інформацію про те, як він може використовуватися або що робить код. Ви можете скористатися кнопкою редагування, щоб зробити майбутні вдосконалення своєї публікації.
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 може бути краще, я просто хочу виправити неправильні відповіді, пов’язані з оболонкою.


1

не впевнений, що це допоможе комусь, окрім уникнення потенційних проблем з непростими символами, які я просто використовував lsдля виконання сортування, але потім використовував файли inodeдля видалення.

У поточному каталозі це зберігає останні 10 файлів на основі часу модифікації.

ls -1tri | awk '{print $1}' | head -n -10 | xargs -n1 -t -Iinode find . -inum inode -exec rm -i {} \;


0

Мені потрібно було елегантне рішення для зайнятого ящика (маршрутизатора), всі рішення 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"

0

Я згоден, що розбір ls - це зло!

Переконайтеся, що на /srv/backupsшляху зберігаються лише останні 5 файлів .

find /srv/backups -maxdepth 1 -type f -printf '%Ts\t%P\n' \
    | sort -rn \
    | tail -n +6 \
    | cut -f2- \
    | xargs -r r

0

На основі відповіді Ісаака Фрімена, але працює над будь-яким каталогом:

ls -1tr $PATH_TO_DELETE/* | head -n -10 | xargs -d '\n' rm -f --

Ви також можете встановити префікс, наприклад $PATH_TO_DELETE/testFi*

Якщо ви використовуєте *після PATH lsзаповіту, виведете абсолютні імена файлів, принаймні в "моєму" баші :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.