Весь сценарий.

Весь сценарий.

У меня есть скрипт, с помощью которого я хочу очищать старые файлы, когда их количество превышает установленный лимит.

У меня есть такая команда:

/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

Связанный контент