
У меня есть скрипт, с помощью которого я хочу очищать старые файлы, когда их количество превышает установленный лимит.
У меня есть такая команда:
/bin/rm -f `/bin/ls -t $bkup_p/mysql.daily/* 2> /dev/null | /bin/awk 'NR>'5`
что работает, и поскольку в $bkup_p может быть пробел, я попытался изменить его на
/bin/rm -f `/bin/ls -t "$bkup_p/mysql.daily/*" 2> /dev/null | /bin/awk 'NR>'5`
Но это не работает. Он не показывает файлы, о которых идет речь, он просто пустой
решение1
Bash не очень удобен, когда дело касается фильтрации файлов по времени изменения и выполнения над ними команд.
Я бы рекомендовал вместо этого попробовать z-shell, так что сначала запустите zsh
, а затем
rm -i -- "$bkup_p"/mysql.daily/*(DN.Om[1,5])
Это удаляет пять самых старых простых файлов в указанном каталоге, что, как я подозреваю, вероятно, является тем, чего вы пытаетесь добиться. Очевидно, в конце дня измените rm -i
на , rm -f
если необходимо.
Чтобы удалить пять последних файлов, сделайте следующее:
rm -i -- "$bkup_p"/mysql.daily/*(DN.om[1,5])
Теперь, как это работает. Внутри все ()
так называемые квалификаторы glob, которые по сути фильтруют файлы в зависимости от ваших потребностей:
D
включить dotfiles (те, которые начинаются с.
)N
не сообщать об ошибке, если совпадений нет.
выбирает только простые файлыom
сортирует по времени изменения (Om
сортирует в обратном порядке)[1,5]
выбирает только пять файлов из списка
Я считаю, что все это должно работать даже со специальными символами в именах файлов (пробелы, переводы строк и т. д.)
решение2
Как уже говорили другие, вам не следует использовать ls
для этого. Но если в вашей среде нет ничего более разумного, и вызнать предостережения, тымощьизбежать этого, если ваши имена файловдостаточно хорошо: никаких пробелов или непечатаемых символов, и особенно ничего, что оболочка интерпретирует как символ подстановки (по крайней мере ?*[]
).
Лучше использовать что-то вроде Perl для сортировки файлов или добавлять даты в имена файлов, чтобы можно было положиться на порядок имен.
С учетом сказанного: во втором фрагменте звездочка заключена в кавычки, поэтому подстановка не происходит. Вы бы увидели ошибку о том, some dir/mysql.daily/*
что не существует, но ее нет, так как вы перенаправили вывод ошибок.
Имена файлов с пробелами создадут проблему, так как вывод подстановки команды будет разделен по пробелам, поэтому some dir/foo
будет выдано что-то rm
вроде двух частей: some
и dir/foo
. Будем надеяться, что ни одна из них не существует, так как они будут удалены.
Если только имя вашего каталога содержит пробелы, вы можете просто cd
сначала войти в него. Или установить, IFS
чтобы он содержал только новую строку (вместо пробела, табуляции, новой строки по умолчанию). Вы упомянули NAS, так что, скорее всего, у вас есть busybox. Это должно работать и в busybox, и в bash, и выводить имена файлов, по одному в строке:
IFS=$'\n'
printf "%s\n" $( ls -t "$bkup_p/mysql.daily/"* | tail -n +6 )
Замените printf "%s\n"
на , rm
чтобы удалить файлы, если вы уверены, что все работает правильно.
решение3
Я бы не рекомендовал парсить вывод, ls
так как он не форматирует вывод должным образом для передачи в новую команду и имеет проблемы с переносимостью.
Я бы попробовал что-то вроде этого вместо этого
find $bkup_p/mysql.daily/ -type f -a -mtime +7 -a -name "*.sql" -a -exec rm -f {} +
примечание:
"*.sql"
измените это по мере необходимости-mtime +7
означает *если этот файл был изменен более (+) 7 дней назад, очевидно, измените его также по мере необходимости
В случае, если вы хотите быть уверены, что у вас всегда есть 10 новейших файлов - несмотря ни на что (и у вас есть GNU find
), вы можете попробовать
find $bkup_p/mysql.daily/ -maxdepth 1 -type f -a -printf "%T+\t%p\n" | sort -r | sed -n '10,$p' | awk '{print $2}' | xargs rm -f
Для получения дополнительной информации о форматировании find
вывода с помощью -printf
оператора см.
man find | less '+/^\s*-printf'
решение4
Проблема неработоспособности *
в том, что когда вы цитируете его, он не расширяется.
Сравните результаты этого: echo *
с этим: echo "*"
.
Надежным решением является использование массива, по одному файлу на каждую позицию массива.
Если вам повезет и вы найдете поддержку этого -printf
:
dir="$bkup_p/mysql.daily/"
find "$dir" -printf '%T@:%i\n'
Все имена файлов будут исключены. Каждый файл будет строкой. И строкой только из цифр, необязательной точки и двоеточия.
Это можно легко отсортировать:
find "$dir" -printf '%T@:%i\n' | sort -rn
Самый простой массив — позиционные аргументы.
Это установит все файлы в каталоге в список позиционных аргументов:
set -f; set -- $(find "$dir" -printf '%T@:%i\n' | sort -n )
Set -f позволит избежать расширения любого имени файла, которое также является символом wildcard *
. Чтобы избежать (удалить) первые 6 имен файлов, только shift
их:
shift 5
И сделайте то, что вам нужно с оставшимися файлами:
for f
do rm -f "$(find "$dir" -inum "${f#*:}")"
done
Весь сценарий.
Весь скрипт (надежен для любого имени файла) становится следующим:
dir="$bkup_p/mysql.daily/"
set -f
set -- $(find "$dir" -type f -printf '%T@:%i\n'|sort -rn)
shift 5
for f
do rm -f "$(find "$dir" -inum "${f#*:}")"
done