【问题标题】:Count and remove old files using Unix find使用 Unix find 计算和删除旧文件
【发布时间】:2012-07-24 17:25:46
【问题描述】:

我想删除$DIR_TO_CLEAN 早于$DAYS_TO_SAVE 天的文件。简单:

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

我想我们可以为rm 添加-type f-f 标志,但我真的很想计算被删除的文件数量。

我们可以天真地这样做:

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

但是这个解决方案还有很多不足之处。除了命令重复之外,如果rm 删除文件失败,这个 sn-p 会高估计数。

我对重定向、管道(包括命名管道)、子shell、xargstee 等非常满意,但我渴望学习新技巧。我想要一个适用于 bash 和 ksh 的解决方案。

你如何计算find删除的文件数量?

【问题讨论】:

标签: bash unix ksh


【解决方案1】:

我会避免使用-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

更新:

kojiro 让我意识到上述解决方案不计算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
)

【讨论】:

  • 此答案将计算 rm 未能删除的文件。此外,作为边缘情况,xargs 受 MAX_ARGS 或其-n 选项的限制。
  • @kojiro:是的,没有检查rm 是否成功。 xargs 自动调用rm 所需的次数,还是我误解了你的评论?
  • 这是一个微妙的问题,但是这个问题与作为骗子提出的“计算文件”问题之间的区别在于,这个问题询问如何仅计算成功删除的文件。
  • 啊,我明白了,没注意到。我添加了一个 bash 解决方案,应该这样做。
  • @Thor,我去掉了尾巴,得到了一个“纯”外壳解决方案。
【解决方案2】:

你可以在 find 中使用 bash:

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

当然,如果找到的文件数大于MAX_ARGS,这可以多次调用bash -c …,如果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 限制的解决方案完全避免了 find。如果您需要它是递归的,则必须使用递归通配符,它​​仅在较新的 shell 中可用。 (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"

这是一种将 find 与 printf 结合使用的方法(严格兼容的 find 没有 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

【讨论】:

  • 我喜欢一个好的单线,但我会选择循环,也许用find 管道进入它。在我之后的任何人都应该最容易维护,避免 MAX_ARGS 问题,并允许轻松访问rm 返回码。谢谢!
  • @Prashant 不要通过管道查找,这很危险:如果您的文件名中包含换行符怎么办?
  • find "$DIR_TO_CLEAN" -mtime +$DAYS_TO_SAVE | while read FILE; do rm "$FILE"; done 引用“$FILE”只处理空格,不处理换行符?
  • 啊,小次郎,我测试并看到了你关于换行符的观点。谢谢你的洞察力。 link 稍后我为任何读者找到。
猜你喜欢
  • 2018-12-28
  • 2014-01-18
  • 2013-08-20
  • 2012-06-10
  • 1970-01-01
  • 1970-01-01
  • 2012-12-16
  • 2011-07-19
  • 1970-01-01
相关资源
最近更新 更多