bash 上的复杂查找
complicated find on bash
我有以下任务:删除旧的 "builds" 超过 30 天。这个解决方案非常有效:
find $jenkins_jobs -type d -name builds -exec find {} -type d -mtime +30 \; >> $filesToBeDelete
cat $filesToBeDelete | xargs rm -rf
但后来添加了一些条件:仅在我们拥有超过 30 个构建并清理最旧的构建时删除。所以在结果中我们应该保留 30 个最新的构建并删除其余的。
我还发现我可以像这样在 find 中使用 if 语句:
if [ $(find bla-bla | wc -l) -gt 30 ]; then
...
fi
但我正在徘徊如何删除这些文件。
清楚了吗?例如,我们在 "build" 文件夹中有 100 个版本,并且所有版本都超过 30 天。所以我想保留 30 个新构建并删除另外 70 个。
这将列出除 30 个最新目录之外的所有目录。
find -type d -name builds -exec ls -d -l --time-style="+%s" {} \;|sed "s#[^ ]\+ \w\+ \w\+ \w\+ \w\+ ##"|sort -r |sed "s#[^ ]\+ ##"|tail -n +31
确定要删除它们后,您可以使用 | xargs rm -rf
它是这样读的:
- 找到所有构建目录
- 用纪元的时间列出它们
- drop (sed - away) 权限,用户,组atc,只留下时间和名字
- 从最新开始按时间排序
- 放弃那些时间
- tail 将显示从 31. 条目开始的所有内容(因此跳过最新的 30 个)
相当hacky,但对于奇怪的文件名应该非常健壮
find -type d -name "builds" -mtime +30 -printf "%T@ %p[=10=]" |\
awk -vRS="[=10=]" -vORS="[=10=]" '{match([=10=],/([^ ]* )(.*)/,a);b[a[2]]=a[1];c[a[1]]=a[2]}END{x=asort(b);for(i=x-30;i>0;i--)print c[b[i]]}' |\
xargs -0 -I{} rm -r {}
我用 echo 测试过,它似乎可以工作,但我会确保它在使用 rm -r 之前显示正确的文件。
所以它所做的是传递以空字符结尾的字符串,以便保留文件名。
主要限制是,如果在同一秒内创建了两个文件,那么它会遗漏一个,因为它使用了关联数组。
这是一个相对安全的答案来列出目录,如果你的stat
足够接近我的(cygwin/bash):
now=$(date +%s)
find $jenkins_jobs -type d -name builds -exec find {} -type d |
while read f; do stat -c'%Y %n' "$f"; done |
sort -nr |
tail -n +31 |
awk $now'->2592000'|
sed 's/^[0-9]* //'
这是使用 date
的 %s
和 stat
的 %Y
提供的纪元时间(自 1970 年以来的秒数)。 sort
和 tail
正在删除最新的 30 个,awk
正在删除任何 30 天或更新的日期。 (2592000 是 30 天的秒数。)最后的 sed
只是删除了 stat
添加的内容,只留下目录名。
我有以下任务:删除旧的 "builds" 超过 30 天。这个解决方案非常有效:
find $jenkins_jobs -type d -name builds -exec find {} -type d -mtime +30 \; >> $filesToBeDelete
cat $filesToBeDelete | xargs rm -rf
但后来添加了一些条件:仅在我们拥有超过 30 个构建并清理最旧的构建时删除。所以在结果中我们应该保留 30 个最新的构建并删除其余的。
我还发现我可以像这样在 find 中使用 if 语句:
if [ $(find bla-bla | wc -l) -gt 30 ]; then
...
fi
但我正在徘徊如何删除这些文件。
清楚了吗?例如,我们在 "build" 文件夹中有 100 个版本,并且所有版本都超过 30 天。所以我想保留 30 个新构建并删除另外 70 个。
这将列出除 30 个最新目录之外的所有目录。
find -type d -name builds -exec ls -d -l --time-style="+%s" {} \;|sed "s#[^ ]\+ \w\+ \w\+ \w\+ \w\+ ##"|sort -r |sed "s#[^ ]\+ ##"|tail -n +31
确定要删除它们后,您可以使用 | xargs rm -rf
它是这样读的:
- 找到所有构建目录
- 用纪元的时间列出它们
- drop (sed - away) 权限,用户,组atc,只留下时间和名字
- 从最新开始按时间排序
- 放弃那些时间
- tail 将显示从 31. 条目开始的所有内容(因此跳过最新的 30 个)
相当hacky,但对于奇怪的文件名应该非常健壮
find -type d -name "builds" -mtime +30 -printf "%T@ %p[=10=]" |\
awk -vRS="[=10=]" -vORS="[=10=]" '{match([=10=],/([^ ]* )(.*)/,a);b[a[2]]=a[1];c[a[1]]=a[2]}END{x=asort(b);for(i=x-30;i>0;i--)print c[b[i]]}' |\
xargs -0 -I{} rm -r {}
我用 echo 测试过,它似乎可以工作,但我会确保它在使用 rm -r 之前显示正确的文件。
所以它所做的是传递以空字符结尾的字符串,以便保留文件名。
主要限制是,如果在同一秒内创建了两个文件,那么它会遗漏一个,因为它使用了关联数组。
这是一个相对安全的答案来列出目录,如果你的stat
足够接近我的(cygwin/bash):
now=$(date +%s)
find $jenkins_jobs -type d -name builds -exec find {} -type d |
while read f; do stat -c'%Y %n' "$f"; done |
sort -nr |
tail -n +31 |
awk $now'->2592000'|
sed 's/^[0-9]* //'
这是使用 date
的 %s
和 stat
的 %Y
提供的纪元时间(自 1970 年以来的秒数)。 sort
和 tail
正在删除最新的 30 个,awk
正在删除任何 30 天或更新的日期。 (2592000 是 30 天的秒数。)最后的 sed
只是删除了 stat
添加的内容,只留下目录名。