Я пытаюсь изменить сценарий Sergiy для действия рекурсивно (в настоящее время, он только удаляет 1 файл, но я хотел бы, чтобы он удалил все файлы, пока макс. предел файла не достигнут). Но я не могу понять это. Я чувствую, что должен смочь изменить первую функцию ("удаляют самый старый") считать несколько меток времени и передачи, что к следующей строке, но я незнаком с этой командой и не могу найти информацию о ней. Любые идеи ценились бы!
Если вы хотите, чтобы скрипт по-прежнему работал в одном каталоге, без повторения через подкаталоги, подсчет и удаление можно выполнить в цикле 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)
. Это будет иметь дело с ситуацией проверки без удаления, но я оставлю это кому-то еще.
Я предложил бы другое решение, которое будет идти рекурсивно в целевой структуре дерева каталогов и удалит все файлы, но кроме предопределенного определенного числа новых файлов. То решение на основе: (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"
Теперь мы можем выполнить некоторые тесты:
Обновление функциональности сценария. Вот представленная обновленная версия вышеупомянутого сценария:
#!/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
не должно быть пустым, чтобы быть перезаписанным поведение по умолчанию.