43

Я пытаюсь держать каталог, полный файлов журнала, управляемым. Ночью я хочу удалить все, кроме 10 самых последних. Как я могу сделать это в одной команде?

9 ответов9

53

Для портативного и надежного решения, попробуйте это:

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 выше (но все еще страдает от новых строк в именах файлов).

20

Код, который вы хотите включить в ваш скрипт:

 rm -f $(ls -1t /path/to/your/logs/ | tail -n +11)

Параметр -1 (числовой) печатает каждый файл в одной строке, чтобы быть в безопасности. Опция -f для rm говорит ему игнорировать несуществующие файлы, когда ls ничего не возвращает.

10

Видимо, разбор 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
9

Такой инструмент, как logrotate, сделает это за вас. Это делает управление журналами намного проще. Вы также можете включить дополнительные процедуры очистки, такие как ореол.

2

Я немного изменил подход Исаака .

Теперь он работает с желаемым путем:

ls -d -1tr /path/to/folder/* | head -n -10 | xargs -d '\n' rm -f
1

Не уверен, что это кому-нибудь поможет, но чтобы избежать потенциальных проблем со странными символами, я просто использовал ls для выполнения сортировки, но затем использую inode для удаления.

Для текущего каталога, он сохраняет последние 10 файлов в зависимости от времени модификации.

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

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
0

В Bash вы можете попробовать:

ls -1t | (i=0; while read f; do
  if [ $i -lt 10 ]; then
    ((i++))
    continue
  else
    rm -f "$f"
  fi
done)

Это пропускает 10 новейших файлов, удаляет остальные. logrotate может быть лучше, я просто хочу исправить неправильные ответы, связанные с оболочкой.

0

Мне нужно было элегантное решение для 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"

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .