Nextrp [CPP] RU + Many GEOs Игра на карте России | NEXTRP

Удалить все, кроме самых последних файлов X в Bash

bash unix scripting

93789 просмотра

16 ответа

70302 Репутация автора

Есть ли простой способ в довольно стандартной среде UNIX с bash выполнить команду, чтобы удалить все, кроме самых последних X-файлов из каталога?

Чтобы привести немного более конкретный пример, представьте себе, что какое-то задание cron записывает файл (скажем, файл журнала или архивную резервную копию) в каталог каждый час. Я хотел бы иметь способ запустить еще одно задание cron, которое бы удаляло самые старые файлы в этом каталоге, пока их не станет меньше, скажем, 5.

И чтобы было ясно, присутствует только один файл, его никогда не следует удалять.

Автор: Matt Sheppard Источник Размещён: 25.08.2008 08:37

Ответы (16)


94 плюса

35222 Репутация автора

Удалите все, кроме 5 (или любого другого числа) самых последних файлов в каталоге.

rm `ls -t | awk 'NR>5'`
Автор: Espo Размещён: 25.08.2008 08:41

85 плюса

6134 Репутация автора

(ls -t|head -n 5;ls)|sort|uniq -u|xargs rm

Эта версия поддерживает имена с пробелами:

(ls -t|head -n 5;ls)|sort|uniq -u|sed -e 's,.*,"&",g'|xargs rm
Автор: thelsdj Размещён: 25.08.2008 08:42

4 плюса

182959 Репутация автора

Если имена файлов не имеют пробелов, это будет работать:

ls -C1 -t| awk 'NR>5'|xargs rm

Если в именах файлов есть пробелы, что-то вроде

ls -C1 -t | awk 'NR>5' | sed -e "s/^/rm '/" -e "s/$/'/" | sh

Основная логика:

  • получить список файлов по времени, один столбец
  • получить все, кроме первых 5 (n = 5 для этого примера)
  • первая версия: отправьте их в rm
  • вторая версия: gen скрипт, который удалит их правильно
Автор: Mark Harrison Размещён: 25.08.2008 08:43

13 плюса

0 Репутация автора

Все эти ответы терпят неудачу, когда есть каталоги в текущем каталоге. Вот то, что работает:

find . -maxdepth 1 -type f | xargs -x ls -t | awk 'NR>5' | xargs -L1 rm

Этот:

  1. работает, когда есть каталоги в текущем каталоге

  2. пытается удалить каждый файл, даже если предыдущий не может быть удален (из-за разрешений и т. д.)

  3. терпит неудачу в безопасности , когда количество файлов в текущем каталоге является чрезмерным и , xargsкак правило , ввернуть Вас более года ( -x)

  4. не учитывает пробелы в именах файлов (возможно, вы используете не ту ОС?)

Автор: Jon Размещён: 18.11.2008 05:44

16 плюса

8865 Репутация автора

find . -maxdepth 1 -type f -printf '%T@ %p\0' | sort -r -z -n | awk 'BEGIN { RS="\0"; ORS="\0"; FS="" } NR > 5 { sub("^[0-9]*(.[0-9]*)? ", ""); print }' | xargs -0 rm -f

Требует GNU find для -printf, GNU sort для -z, GNU awk для "\ 0" и GNU xargs для -0, но обрабатывает файлы со встроенными символами новой строки или пробелами.

Автор: wnoise Размещён: 18.11.2008 07:51

8 плюса

5533 Репутация автора

Игнорирование новых строк игнорирует безопасность и хорошее кодирование. У wnoise был единственный хороший ответ. Вот вариант его, который помещает имена файлов в массив $ x

while IFS= read -rd ''; do 
    x+=("${REPLY#* }"); 
done < <(find . -maxdepth 1 -printf '%T@ %p\0' | sort -r -z -n )
Автор: Ian Kelling Размещён: 13.06.2009 12:03

2 плюса

5117 Репутация автора

С зш

Предполагая, что вы не заботитесь о существующих каталогах и у вас будет не более 999 файлов (выберите большее число, если хотите, или создайте цикл while).

[ 6 -le `ls *(.)|wc -l` ] && rm *(.om[6,999])

В *(.om[6,999]), на .средства файлов, то oсредство порядок сортировки вверх, mсредства по дате модификации (положить aна время доступа или cдля изменения инода), то [6,999]выбирает диапазон файла, поэтому не Р.М. 5 первых.

Автор: lolesque Размещён: 21.11.2011 06:08

58 плюса

3495 Репутация автора

Более простой вариант ответа thelsdj:

ls -tr | head -n -5 | xargs --no-run-if-empty rm 

ls -tr отображает все файлы, сначала самые старые (сначала -t самые новые, -r наоборот).

head -n -5 отображает все, кроме 5 последних строк (то есть 5 новейших файлов).

xargs rm вызывает rm для каждого выбранного файла.

Автор: Fabien Размещён: 12.04.2012 08:25

0 плюса

51 Репутация автора

leaveCount=5
fileCount=$(ls -1 *.log | wc -l)
tailCount=$((fileCount - leaveCount))

# avoid negative tail argument
[[ $tailCount < 0 ]] && tailCount=0

ls -t *.log | tail -$tailCount | xargs rm -f
Автор: Pavel Tankov Размещён: 07.06.2013 07:34

12 плюса

4257 Репутация автора

ls -tQ | tail -n+4 | xargs rm

Список имен файлов по времени модификации, цитируя каждое имя файла. Исключить первые 3 (3 самых последних). Удалить оставшиеся.

РЕДАКТИРОВАТЬ после полезного комментария от mklement0 (спасибо!): Исправлен аргумент -n + 3, и обратите внимание, что это не будет работать должным образом, если имена файлов содержат символы новой строки и / или каталог содержит подкаталоги.

Автор: Mark Размещён: 25.07.2013 05:51

94 плюса

159375 Репутация автора

Решение

Проблемы с существующими ответами:

  • невозможность обрабатывать имена файлов со встроенными пробелами или символами новой строки.
    • в случае решений, которые вызываются rmнепосредственно в подстановке команд без кавычек ( rm `...`), существует дополнительный риск непреднамеренного сглаживания.
  • невозможность различать файлы и каталоги (т. е. если каталоги оказались в числе 5 самых последних измененных элементов файловой системы, вы фактически сохраните менее 5 файлов, и применение rmк каталогам не удастся).

Ответ wnoise решает эти проблемы, но решение является специфичным для GNU (и довольно сложным).

Вот прагматичное, POSIX-совместимое решение, которое поставляется только с одной оговоркой : оно не может обрабатывать имена файлов со встроенными символами новой строки - но я не считаю это реальной проблемой для большинства людей.

Для справки, вот объяснение того, почему вообще не очень хорошая идея анализировать lsвывод: http://mywiki.wooledge.org/ParsingLs

ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}

Вышеупомянутое неэффективно , потому что xargsдолжен вызывать rmодин раз для каждого имени файла.
Ваша платформа xargsможет позволить вам решить эту проблему:

Если у вас есть GNU xargs , используйте -d '\n', что делает xargsкаждую входную строку отдельным аргументом, но передает столько аргументов, сколько поместится в командной строке одновременно :

ls -tp | grep -v '/$' | tail -n +6 | xargs -d '\n' -r rm --

-r( --no-run-if-empty) гарантирует, что rmне вызывается, если нет ввода.

Если у вас есть BSD xargs (в том числе и в OS X ), вы можете использовать -0для обработки NUL-разделенного ввода после первой трансляции символов новой строки в NUL( 0x0) chars., Который также передает (как правило) все имена файлов одновременно (также будет работать с GNU xargs):

ls -tp | grep -v '/$' | tail -n +6 | tr '\n' '\0' | xargs -0 rm --

Объяснение:

  • ls -tpпечатает имена элементов файловой системы, отсортированные по тому, как недавно они были изменены, в порядке убывания (сначала самые последние измененные элементы) ( -t), с каталогами, напечатанными с последующим /знаком, чтобы пометить их как таковые ( -p).
  • grep -v '/$'затем отсеивает каталоги из результирующего списка, пропуская -vстроки ( ), которые имеют конечный /( /$).
    • Предостережение : поскольку символическая ссылка, которая указывает на каталог , технически сама по себе не является каталогом, такие символические ссылки не будут исключены.
  • tail -n +6пропускает первые 5 записей в списке, фактически возвращая все, кроме 5 самых последних измененных файлов, если таковые имеются.
    Обратите внимание, что для исключения Nфайлов N+1необходимо передать в tail -n +.
  • xargs -I {} rm -- {}(и его варианты) затем вызывает rmвсе эти файлы; если совпадений xargsнет вообще, ничего не сделаю.
    • xargs -I {} rm -- {}определяет местозаполнитель, {}который представляет каждую входную строку как единое целое , поэтому rmзатем вызывается один раз для каждой входной строки, но с именами файлов со встроенными пробелами, которые обрабатываются правильно.
    • --во всех случаях гарантирует , что любые имена файлов , которые происходят , чтобы начать с -не ошибаемся для опций по rm.

Вариации на исходной задаче, в случае , если соответствующие файлы должны быть обработаны по отдельности или собран в массиве оболочки :

# One by one, in a shell loop (POSIX-compliant):
ls -tp | grep -v '/$' | tail -n +6 | while IFS= read -r f; do echo "$f"; done

# One by one, but using a Bash process substitution (<(...), 
# so that the variables inside the `while` loop remain in scope:
while IFS= read -r f; do echo "$f"; done < <(ls -tp | grep -v '/$' | tail -n +6)

# Collecting the matches in a Bash *array*:
IFS=$'\n' read -d '' -ra files  < <(ls -tp | grep -v '/$' | tail -n +6)
printf '%s\n' "${files[@]}" # print array elements
Автор: mklement0 Размещён: 18.01.2016 07:24

0 плюса

341 Репутация автора

Я сделал это в скрипт оболочки bash. Использование: keep NUM DIRгде NUM - это количество файлов для хранения, а DIR - каталог для очистки.

#!/bin/bash
# Keep last N files by date.
# Usage: keep NUMBER DIRECTORY
echo ""
if [ $# -lt 2 ]; then
    echo "Usage: $0 NUMFILES DIR"
    echo "Keep last N newest files."
    exit 1
fi
if [ ! -e $2 ]; then
    echo "ERROR: directory '$1' does not exist"
    exit 1
fi
if [ ! -d $2 ]; then
    echo "ERROR: '$1' is not a directory"
    exit 1
fi
pushd $2 > /dev/null
ls -tp | grep -v '/' | tail -n +"$1" | xargs -I {} rm -- {}
popd > /dev/null
echo "Done. Kept $1 most recent files in $2."
ls $2|wc -l
Автор: Bulrush Размещён: 03.02.2016 05:47

1 плюс

11 Репутация автора

нашел интересный cmd в Sed-Onliners - удалите последние 3 строки - и он идеально подходит для другого способа облысения кошки (хорошо, нет), но идея:

 #!/bin/bash
 # sed cmd chng #2 to value file wish to retain

 cd /opt/depot 

 ls -1 MyMintFiles*.zip > BigList
 sed -n -e :a -e '1,2!{P;N;D;};N;ba' BigList > DeList

 for i in `cat DeList` 
 do 
 echo "Deleted $i" 
 rm -f $i  
 #echo "File(s) gonzo " 
 #read junk 
 done 
 exit 0
Автор: tim Размещён: 01.09.2016 09:09

2 плюса

372 Репутация автора

Я понимаю, что это старая ветка, но, возможно, кому-то это поможет. Эта команда найдет файлы в текущем каталоге:

for F in $(find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n' | sort -r -z -n | tail -n+5 | awk '{ print $2; }'); do rm $F; done

Это немного более надежно, чем некоторые из предыдущих ответов, поскольку позволяет ограничить область поиска файлами, соответствующими выражениям. Сначала найдите файлы, соответствующие любым условиям, которые вы хотите. Распечатайте эти файлы с отметками времени рядом с ними.

find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n'

Затем отсортируйте их по временным меткам:

sort -r -z -n

Затем удалите 4 последних файла из списка:

tail -n+5

Возьмите 2-й столбец (имя файла, а не метку времени):

awk '{ print $2; }'

А затем оберните все это в утверждение for:

for F in $(); do rm $F; done

Это может быть более многословная команда, но мне повезло больше, когда я смог нацелиться на условные файлы и выполнить с ними более сложные команды.

Автор: TopherGopher Размещён: 10.01.2017 10:55

1 плюс

11 Репутация автора

Удаляет все, кроме 10 последних (большинство последних) файлов

ls -t1 | head -n $(echo $(ls -1 | wc -l) - 10 | bc) | xargs rm

Если менее 10 файлов, файл не будет удален, и у вас будет: error head: недопустимое количество строк - 0

Считать файлы с помощью bash

Автор: fabrice Размещён: 29.05.2017 05:14

1 плюс

64 Репутация автора

Мне нужно было элегантное решение для 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"
Автор: Pila Размещён: 03.10.2018 05:48
Вопросы из категории :
32x32