Я настроил ежедневную работу cron для резервного копирования моего сервера.

В моей резервной копии папки команда резервного копирования генерирует 2 файла: сам архив .tar.gz и файл .info.json, как показано ниже:

-rw-r--r-- 1 root root     1617 Feb  2 16:17 20200202-161647.info.json
-rw-r--r-- 1 root root 48699726 Feb  2 16:17 20200202-161647.tar.gz
-rw-r--r-- 1 root root     1617 Feb  3 06:25 20200203-062501.info.json
-rw-r--r-- 1 root root 48737781 Feb  3 06:25 20200203-062501.tar.gz
-rw-r--r-- 1 root root     1618 Feb  4 06:25 20200204-062501.info.json
-rw-r--r-- 1 root root 48939569 Feb  4 06:25 20200204-062501.tar.gz

Как написать скрипт bash, который будет хранить только последние 2 архива и удалит все остальные резервные копии (targ.gz и info.json).

В этом примере это будет означать удаленные 20200204-062501.info.json и 20200204-062501.tar.gz.

Редактировать:

Я заменяю -name на -wholename в скрипте, но когда я его запускаю, он, очевидно, не имеет никаких эффектов. Старые архивы все еще там, и они не были удалены.

Сценарий :

#!/bin/bash

DEBUG="";
DEBUG="echo DEBUG...";    #put last to safely debug without deleting files
keep=2;
for suffix in /home/archives .json .tar; do
    list=( $( find . -wholename "*$suffix" ) ); #allow for zero names
    if [ ${#list[@]} -gt $keep ]; then
        # delete all but last $keep oldest files
        ${DEBUG}rm -f "$( ls -tr "${list[@]}" | head -n-$keep )";
    fi
done

Изменить 2:

Если я запускаю скрипт @sorin, действительно ли он удаляет все, если я верю выводу скрипта?

Папка архива перед запуском скрипта:

https://pastebin.com/7WtwVHCK

Скрипт, который я запускаю:

find home/archives/ \( -name '*.json' -o -name '*.tar.gz' \) -print0 |\
    sort -zr |\
    sed -z '3,$p' | \
    xargs -0 echo rm -f

Вывод скрипта:

https://pastebin.com/zd7a2zcq

Изменить 3:

Команда find /home/archives/ -daystart \( -name '*.json' -o -name '*.tar.gz' \) -mtime +1 -exec echo rm -f {} + работает и выполняет свою работу.

Помечено как решенное

1
qwerty1805 4 Фев 2020 в 18:19

4 ответа

Лучший ответ

Если файл генерируется ежедневно, простой подход заключается в использовании условия поиска -mtime:

find /home/archives/ -daystart \( -name '*.json' -o -name '*.tar.gz' \) -mtime +1 -exec echo rm -f {} +
  • -daystart - используйте начало дня для сравнения времени модификации
  • \( -name '*.json' -o -name '*.tar.gz' \) - выберите файлы, заканчивающиеся на *.json или *.tar.gz
  • -mtime +1 - время модификации старше 24 часов (с начала дня)
  • -exec echo rm -f {} + - удалите файлы (удалите echo после тестирования и проверки результата, что вы хотите)

Более простое решение, позволяющее избежать ls и его подводных камней и не зависящее от времени модификации файлов:

find /home/archives/ \( -name '*.json' -o -name '*.tar.gz' \) -print0 |\
    sort -zr |\
    sed -nz '3,$p' | \
    xargs -0 echo rm -f
  • \( -name '*.json' -o -name '*.tar.gz' \) - найдите файлы, заканчивающиеся на *.json или tar.gz
  • -print0 - вывести их через ноль
  • sort -zr - -z говорит sort использовать ноль в качестве разделителя строк, -r сортирует их в обратном порядке
  • sed -nz '3,$p' - -z такой же, как указано выше. '3,$p' - вывести строки между 3-м и концом ($)
  • xargs -0 echo rm -f - выполнить rm с переданными аргументами (удалите эхо после того, как вы проверили, и вы удовлетворены командой)

Примечание: не все sort и sed поддерживают -z, но большинство поддерживают. Если вы застряли в такой ситуации, вам, возможно, придется использовать язык более высокого уровня

1
Sorin 8 Фев 2020 в 18:05

Найдите два последних файла в пути:

most_recent_json=$(ls -t *.json | head -1)
most_recent_tar_gz=$(ls -t *.tar.gz | head -1)

Удалите все остальное, игнорируя найденные последние файлы:

rm -i $(ls -I $most_recent_json -I $most_recent_tar_gz)
0
gorn 4 Фев 2020 в 16:42

Автоматическое удаление может быть опасным для вашего психического состояния, если оно удаляет ненужные файлы или прерывает длинные сценарии на ранней стадии из-за непредвиденных ошибок. Скажите, когда в вашем примере меньше 1 + 2 файлов. Убедитесь, что скрипт не завершится ошибкой, если файлов вообще нет.

tdir=/home/archives/; #target dir DEBUG=""; DEBUG="echo DEBUG..."; #put last to safely debug without deleting files keep=2; for suffix in .json .tar; do list=( $( find "$tdir" -name "*$suffix" ) ); #allow for zero names if [ ${#list[@]} -gt $keep ]; then # delete all but last $keep oldest files ${DEBUG}rm -f "$( ls -tr "${list[@]}" | head -n-$keep )"; fi done

0
Gilbert 10 Фев 2020 в 12:46

Предполагая, что у вас есть менее 10 файлов и что они созданы в парах, вы можете сделать что-то простое, как это:

files_to_delete=$(ls -tr1 | tail -n+3)
rm $files_to_delete

-tr1 указывает команду ls перечислить файлы в обратном хронологическом порядке по времени модификации, каждый в одной строке.

tail -n+3 сообщает хвостовой команде запустить на третьей строке (пропуская первые две строки).

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

0
Greg Tarsa 10 Фев 2020 в 17:12