Подсчитайте и удалите старые файлы, используя Unix find

Я хочу удалить файлы в $DIR_TO_CLEAN старше чем $DAYS_TO_SAVE дней. Легко:

find "$DIR_TO_CLEAN" -mtime +$DAYS_TO_SAVE -exec rm {} \;

Я полагаю, мы могли бы добавить -type f или -f флаг для rm, но я бы очень хотел посчитать количество удаляемых файлов.

Мы могли бы сделать это наивно

DELETE_COUNT=`find "$DIR_TO_CLEAN" -mtime +$DAYS_TO_SAVE | wc -l`
find "$DIR_TO_CLEAN" -mtime +$DAYS_TO_SAVE -exec rm {} \;

Но это решение оставляет желать лучшего. Помимо дублирования команд, этот фрагмент переоценивает количество, если rm не удалось удалить файл.

Я неплохо себя чувствую с перенаправлением, трубами (в том числе именованными), подоболочками, xargs, tee и т. д., но я хочу научиться новым трюкам. Я хотел бы решение, которое работает как на bash, так и на ksh.

Как бы вы посчитали количество файлов, удаленных find ?

2 ответа

Решение

Вы можете просто использовать bash в find:

find "$DIR_TO_CLEAN" -mtime +$DAYS_TO_SAVE -exec bash -c 'printf "Total: %d\n" $#; rm "$@"' _ {} +

Конечно это можно назвать bash -c … более одного раза, если количество найденных файлов больше, чем MAX_ARGS, и это также может переоценить количество, если rm не удается. Но решение этих проблем становится грязным:

find "$DIR_TO_CLEAN" -mtime +$DAYS_TO_SAVE -exec bash -c 'printf "count=0; for f; do rm "$f" && (( count++ )); done; printf "Total: %d\n" $count' _ {} +

Это решение, позволяющее избежать ограничений MAX_ARGS, позволяет избежать поиска в целом. Если вам нужно, чтобы он был рекурсивным, вам придется использовать рекурсивное сглаживание, которое доступно только в новых оболочках. (globstar это особенность bash 4)

shopt -s globstar
# Assume DAYS_TO_SAVE reformatted to how touch -m expects it. (Exercise for the reader.)
touch -m "$DAYS_TO_SAVE" referencefile
count=0
for file in "$DIR_TO_CLEAN/"**/*; do
    if [[ referencefile -nt "$file" ]]; then
        rm "$file" && (( count++ ))
    fi
done
printf 'Total: %d\n' "$count"

Вот подход, использующий поиск с printf (строго совместимый поиск не имеет printf, но вы можете использовать printf как отдельную утилиту в этом случае).

find "$DIR_TO_CLEAN" -type -f -mtime "+$DAYS_TO_SAVE" -exec rm {} \; -printf '.' | wc -c
find "$DIR_TO_CLEAN" -type -f -mtime "+$DAYS_TO_SAVE" -exec rm {} \; -exec printf '.' \; | wc -c

Я бы избежал -exec и пойти на трубопроводное решение:

find "$DIR_TO_CLEAN" -type f -mtime +$DAYS_TO_SAVE -print0 \
| awk -v RS='\0' -v ORS='\0' '{ print } END { print NR }'  \
| xargs -0 rm

С помощью awk считать матчи и передавать их rm,

Обновить:

Кодзиро дал мне понять, что вышеуказанное решение не учитывает частоту успеха / неудачи rm, Как awk есть проблемы с плохо именованными файлами, я думаю, что следующее bash Решение может быть лучше:

find "${DIR_TO_CLEAN?}" -type f -mtime +${DAYS_TO_SAVE?} -print0 |
(
  success=0 fail=0
  while read -rd $'\0' file; do 
  if rm "$file" 2> /dev/null; then 
    (( success++ ))
  else
    (( fail++ ))
  fi
  done
  echo $success $fail
)
Другие вопросы по тегам