如何删除除最近创建的文件之外的所有具有模式的文件
ls -l
total 655748
-rw-r--r-- 1 jetty jetty 120579643 May 10 19:59 2023_05_10.jetty.log
-rw-r--r-- 1 jetty jetty 115205809 May 11 19:59 2023_05_11.jetty.log
-rw-r--r-- 1 jetty jetty 102921116 May 12 19:59 2023_05_12.jetty.log
-rw-r--r-- 1 jetty jetty 85266768 May 13 19:59 2023_05_13.jetty.log
-rw-r--r-- 1 jetty jetty 97032182 May 14 19:59 2023_05_14.jetty.log
-rw-r--r-- 1 jetty jetty 117095164 May 15 19:59 2023_05_15.jetty.log
-rw-r--r-- 1 jetty jetty 33339025 May 16 04:13 2023_05_16.jetty.log
答案1
假设你确实需要文件创建时间,你需要Ubuntu 20.10 或更高版本,因为之前 GNU coreutils 工具无法访问文件创建时间。
但在 20.10 及更新版本中,您可以使用:
find some/directory -type f -exec stat --format '%W' {} \; -printf "%p\0" |
sort -zn |
sed -z 's/^[0-9.]*\n//; $d' |
xargs -r0 echo rm --
- 整个管道使用空分隔数据,这是处理文件名最安全的方式
- GNU
find
无法访问 Linux 上的出生时间但是,所以我们使用它stat
来打印出生时间,然后使用find
它来打印文件名并用空字符来分隔它 - 然后我们按数字对数据进行排序(基于创建时间)
- 然后我们删除出生时间前缀,只留下文件名,并删除最后一行,即最新的文件
- 然后我们使用
xargs
删除rm
所有这些文件。上面的命令使用echo rm
显示将要运行的命令 — 删除echo
以实际运行rm
并删除文件。
答案2
这个问题可以通过使用按最近修改的顺序列出文件的ls
选项来解决。通过该选项,我们可以排除当前目录中可能存在的任何子目录。-t
-p
经过我的测试,它看起来应该是这样的:
[11:55:08] /home/jaska/src/foo/> ls -tp
baz.log bar/ bar.log foo.log foo barbaz
接下来我们需要使用grep
来摆脱不需要的目录:
[11:55:16] /home/jaska/src/foo/> ls -tp | grep -v '/$'
baz.log
bar.log
foo.log
foo
barbaz
然后我们必须声明模式,再次使用grep
:
[11:55:28] /home/jaska/src/foo/> ls -tp | grep -v '/$' | grep \.log
baz.log
bar.log
foo.log
因为它们已经处于正确的顺序,所以我们习惯tail
从下往上删除一定量:
[11:55:21] /home/jaska/src/foo/> ls -tp | grep -v '/$' | grep \.log | tail -n +2
bar.log
foo.log
最后我们删除指定的文件:
[11:52:05] /home/jaska/src/foo/> ls -tp | grep -v '/$' | grep \.log | tail -n +2 | xargs -I {} rm -- {}
[12:01:59] /home/jaska/src/foo/> ll
total 4
drwxrwxr-x 2 jaska jaska 4096 May 16 11:53 bar/
-rw-rw-r-- 1 jaska jaska 0 May 16 11:48 barbaz
-rw-rw-r-- 1 jaska jaska 0 May 16 11:54 baz.log
-rw-rw-r-- 1 jaska jaska 0 May 16 11:50 foo
您可能会注意到,唯一剩下的 .log 文件未列在最新输出中,因为它是最近修改的。
回顾一下,要使用的完整命令是:
ls -tp | grep -v '/$' | grep \.log | tail -n +2 | xargs -I {} rm -- {}
您可能需要修改第二grep
部分以适合某些日志文件,但从您提供给我们的列表来看,这应该足够了。
致谢及各部分的完整解释可在此处找到:https://stackoverflow.com/questions/25785/delete-all-but-the-most-recent-x-files-in-bash
答案3
注释 # 1
您在输出中实际看到的ls -l
是文件的最后修改日期和不是创建日期。
find
具有模式匹配(通配符)就像-name "*jetty.log"
并且它已经-mtime
修改了好几天了......所以,例如:
find . -mtime +0 -type f -name "*jetty.log"
查找 24 小时前修改过的匹配文件...1 代表 48 小时,依此类推。
并且它已经-mmin
修改了几分钟...所以,例如:
find . -mmin +10 -type f -name "*jetty.log"
查找 10 分钟前修改的匹配文件。
并且它具有-delete
删除匹配文件的操作...例如:
find . -mtime +9 -type f -name "*jetty.log" -delete
查找10天前修改过的匹配文件并删除它们。
您可以通过添加来限制搜索到当前文件夹的第一级,-maxdepth 1
以防止在子目录中匹配文件。
笔记2
我注意到您的文件名以日期模式开头,例如2923_05_10...
...如果这是一致的并且您想要按这些日期匹配,那么您可以使用模式匹配,例如:
find . -maxdepth 1 -type f -name "2023_05_1[0-4].jetty.log"
匹配名称中2023_05_10
带有 to 的文件。2023_05_14
由于文件名中的这些日期模式可以轻松解析为实际日期,因此您也可以按bash
如下方式执行此操作,例如:
#!/bin/bash
days=5 # Set the number of days from now for a date to be considered as recent
for f in *.jetty.log
do
f1="${f%%.*}"
f1="${f1//_/-}"
fd="$(date -d "$f1" '+%s')"
td="$(date -d "- $days days" '+%s')"
[ -f "$f" ] && [ "$fd" -lt "$td" ] && echo rm -- "$f"
done
匹配并打印名称中日期超过 5 天的文件...如果输出是您想要的,则删除echo
并重新运行它以删除这些文件。
注释 # 3
如果您的意思实际上是文件的创建/出生日期,那么您可以这样做,bash
例如:
#!/bin/bash
days=5 # Set the number of days from now for a date to be considered recent
for f in *.jetty.log
do
cd="$(stat -c '%w' "$f")"
fcd="$(date -d "$cd" '+%s')"
td="$(date -d "- $days days" '+%s')"
[ -f "$f" ] && [ "$fcd" -lt "$td" ] && echo rm -- "$f"
done
匹配并打印创建日期超过 5 天的文件...如果输出是您想要的,则删除echo
并重新运行它以删除这些文件。
但是,这个需要较新的内核,因为stat
旧内核不支持访问出生日期...请参阅这个答案以获得关于此问题的更多解释。
注释 # 4
使用补充缺少的系统工具功能的功能来简化您的生活bash
...例如,此bash
功能:
function rm_old {
lfcd=0
for f in *
do
if [ -f "$f" ]
then
fcd="$(stat -c '%w' "$f")"
fcd="$(date -d "$fcd" '+%s')"
if [ "$fcd" -gt "$lfcd" ]
then
todelete+=("${tokeep[@]}")
tokeep=("$f")
lfcd="$fcd"
elif [ "$fcd" -eq "$lfcd" ]
then
tokeep+=("$f")
else
todelete+=("$f")
fi
fi
done
echo "These files will be DELETED:"
printf '\e[4;31m%s\e[0m\n' "${todelete[@]}"
echo "This/These file(s) will be KEPT:"
printf '\e[4;32m%s\e[0m\n' "${tokeep[@]}"
read -p "Confirm Y(Yes)/N(No)" answer
case "$answer" in
Yes|yes|YES|Y|y)
[ "${#todelete[@]}" -gt 0 ] && rm -- "${todelete[@]}" || echo "Exiting ..."
;;
*)
echo "Aborting ..."
;;
esac
unset answer
unset todelete
unset tokeep
unset fcd
unset lfcd
}
将仅保留当前工作目录中最后创建的文件并删除其余文件。