Удалите самые старые файлы рекурсивно, пока макс. предел не будет достигнут

Я пытаюсь изменить сценарий Sergiy для действия рекурсивно (в настоящее время, он только удаляет 1 файл, но я хотел бы, чтобы он удалил все файлы, пока макс. предел файла не достигнут). Но я не могу понять это. Я чувствую, что должен смочь изменить первую функцию ("удаляют самый старый") считать несколько меток времени и передачи, что к следующей строке, но я незнаком с этой командой и не могу найти информацию о ней. Любые идеи ценились бы!

3
задан 27 October 2017 в 12:20

2 ответа

Если вы хотите, чтобы скрипт по-прежнему работал в одном каталоге, без повторения через подкаталоги, подсчет и удаление можно выполнить в цикле while. Последний раздел функции main должен быть изменен, чтобы он выглядел следующим образом:

local file_inodes=$(get_files)
    while filecount_above_limit 
    do
        printf  "@@@ File count in %s is above %d." "$directory" $max_files
        printf "Will delete oldest\n"
        sort -k1 -n <<< "$file_inodes" | delete_oldest
        local file_inodes=$(get_files)
    done
    printf "@@@ File count in %s is below %d."  "$directory" $max_files
    printf "Exiting normally"

Предупреждение!
Проблема с этим простым изменением заключается в том, что, если Вы не прокомментировали строку удаления вверху, скрипт будет бесконечно зацикливаться, так как он пересчитывает количество файлов после каждого удаления. Если файл не удален, количество файлов остается неизменным, и цикл никогда не завершается.

Можно изменить сценарий более сложным способом, удалить индексный файл из массива file_inodes после удаления и отрицательно увеличить переменную file_count вместо повторения строки local file_inodes=$(get_files). Это будет иметь дело с ситуацией проверки без удаления, но я оставлю это кому-то еще.

4
ответ дан 1 December 2019 в 15:22

Я предложил бы другое решение, которое будет идти рекурсивно в целевой структуре дерева каталогов и удалит все файлы, но кроме предопределенного определенного числа новых файлов. То решение на основе: (1) Рекурсивного сценария удара и (2) Объяснения сценария оболочки для рекурсивной печати полного дерева каталогов.

1. Создайте исполняемый файл сценария, названный walkr (обойдите и удалите), который расположен в /usr/local/bin быть доступным как команда оболочки (более подробные шаги).

2. Содержание сценария walkr довольно просто:

#!/bin/bash

[[ -z "${NFK}" ]] && NFK='7' || NFK="$NFK"
[[ -z "${1}" ]] && ABS_PATH="${PWD}" || cd "${1}" && ABS_PATH="${PWD}"

file_operations() {
        local IFS=$'\t\n' # Change the value of the Internal Field Separator locally
        rm $(ls -lt | grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' | tail -n +"$((NFK+1))") 2>/dev/null
}

walk() {
        cd "$1" && file_operations # Change directory to the destination path and call the above function
        for item in "$1"/*; do [[ -d "$item" ]] && walk "$item"; done # Make the recursion
}

walk "${ABS_PATH}"

3. Объяснение:

  • В начале сценарий проверит если переменная $NFK (что определенный количество файлов, которые будут сохранены), установлен заранее - условие [[ -z "${NFK}" ]]. Если это не установило значение по умолчанию, 7.

  • Затем сценарий имеет дело с целевым путем (stdin команды). Если это не обеспечивается - условие [[ -z "${1}" ]] - сценарий будет работать в текущий каталог.

  • Наконец основная функция walk() будет выполняться.


  • Функция walk():

    • Первоначально это изменит каталог на целевой путь cd "$1" и затем это вызовет функцию file_operations(), это будет работать внутри.

    • Далее, для каждого $item, в текущем каталоге "$1"/*, который является также каталогом [[ -d "$item" ]] функция walk() будет выполняться снова, таким образом мы создаем рекурсию.

  • Функция file_operations():

    • Первоначально это установит локальное значение внутренней переменной Bash $IFS, таким образом мы можем правильно обработать <the list of the files to be removed>, неважно, в отдельных именах файлов существуют пробелы.

    • Далее команда rm $(<the list of the files to be removed>) будет выполняться. Перенаправление ошибок 2>/dev/null для этих случаев, когда нет ничего для удаления. <the list of the files to be removed> взят таким образом:

      • Команда ls -lt перечислит содержание текущего каталога с длинным форматом списка -l и список будет отсортирован по времени изменения, новейшему сначала -t. И этот список передается по каналу | к следующей команде.

      • Следующая команда grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' обрежет эти строки, которые начинаются ^ с -, с их начала к шаблон [0-9]{2}:[0-9]{2}_‡. Опция -P с опцией -o произведет строки, которые соответствуют к шаблону ^-.*[0-9]{2}:[0-9]{2}_. \K уведомьте проигнорирует подобранную часть, прибывшую перед собой. (источник - этот полезный ответ)

        †Thus мы получим только названия файлов из списка. В выводе ls -l строки, которые описывают каталоги, запускаются с d, и они для файлов запускаются с -. (источник идеи)

        Этот шаблон соответствует к формату времени 00:00.

      • Наконец команда tail -n +"$((NFK+1)) сократит первые несколько строк нашего списка файлов. Количество этих первых нескольких строк равно значению $NFK плюс 1, это - требование команды tail.

4. Примеры использования:

  • Работать walkr для текущего каталога:

    walkr      # You shouldn't use any argument, 
    walkr ./   # but you can use also this format
    
  • Работать walkr для любого дочернего каталога:

    walkr <directory name>
    walkr ./<directory name>
    walkr <directory name>/<sub directory>
    
  • Работать walkr для любого другого каталога:

    walkr /full/path/to/<directory name>
    
  • Изменить количество файлов, которые будут сохранены (к 3 например), используйте этот формат

    NFK=3 walkr
    NFK=3 walkr /full/path/to/<directory name>
    # etc.
    

5. Давайте играть со сценарием walkr:

  • Мы можем использовать команду touch file.name -d "1 hour ago" создать пустой файл, датированный один час назад. Таким образом, мы можем использовать следующие команды для создания структуры каталогов, поскольку это представило здесь.

    mkdir -p ~/temp/dir{A..C} && cd ~/temp ;\
    DEST='';      touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago" ;\
    DEST='dirA/'; touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago" ;\
    DEST='dirB/'; touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago" ;\
    DEST='dirC/'; touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago"
    
  • Теперь мы можем выполнить некоторые тесты:

    enter image description here


Обновление функциональности сценария. Вот представленная обновленная версия вышеупомянутого сценария:

#!/bin/bash

[[ -z "${1}" ]] && ABS_PATH="${PWD}" || cd "$1" && ABS_PATH="${PWD}"
[[ -z "${2}" ]] && NFK='7' || NFK="$2"  # Number of the files to be kept
[[ -z "${3}" ]] && REC='1' || REC="$3"  # REC='1' - work recursively
[[ -z "${4}" ]] && VRB='1' || VRB="$4"  # VRB='1' - work in verbose mode

file_operations() {
        local IFS=$'\t\n' # Change the value of the Internal Field Separator locally

        if [ "$VRB" == "1" ]
        then # Verbose mode:
                rm -v $(ls -lt | grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' | tail -n +"$((NFK+1))") 2>/dev/null && printf " -from: '%s' \n" "$1" || echo "nothing to remove in: '$1'"
        else # Quiet mode:
                rm $(ls -lt | grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' | tail -n +"$((NFK+1))") 2>/dev/null
        fi
}

walk() {
        # Change directory to the destination path and call the above function, pass $1 for the verbose mode
        cd "$1" && file_operations "$1"
        # If REC='1': Recursive mode -- Make the recursion; otherwise work on the curent level
        if [ "$REC" == "1" ]; then for item in "$1"/*; do [[ -d "$item" ]] && walk "$item"; done; fi
}

walk "${ABS_PATH}"
  • Эта версия сценария может обработать еще немного входных переменных. Это имеет тихие и подробные режимы и может работать не рекурсивно.

  • Полный формат:

    walkr '<destination path>' '<number of lines to be kept>' '<no recursion>' '<quiet>'
    

    Где точное содержание <no recursion> и <quiet> имеет, неважно. Просто входные переменные $3 и $4 не должно быть пустым, чтобы быть перезаписанным поведение по умолчанию.

2
ответ дан 1 December 2019 в 15:22

Другие вопросы по тегам:

Похожие вопросы: