我想删除不同目录中的所有文件,并且只想保留最新的文件 20 个文件。这是执行此操作的正确命令吗?
ls -t1 /mnt/dwh/ftp/dwh_ftp_cbs/ARLOGS/ | tail -n +22 | xargs rm -f
答案1
与zsh
和全局限定符:
print -rl -- *(D.Om[1,-21])
将列出除最后(最近修改的)二十个之外的所有常规文件。
D
选择隐藏文件,.
仅选择常规文件,Om
意味着反向排序时间(最旧的在前)并[1,-21]
从第一个到倒数第 21 个进行选择。
如果您对结果满意,请替换print -rl
为rm
:
rm -- *(D.Om[1,-21])
如果您有大量文件,您可能必须使用zargs
以避免参数列表太长:
autoload zargs
zargs ./*(D.Om[1,-21]) -- rm
答案2
在你的命令中有很多缺点,例如 的输出的处理ls
,它是一种教义问答。看本文为什么不这样做呢?
这种方法使用不GNU 扩展,并在 bash、zsh、ksh93、dash 和 ash 上进行了测试:
for f in * .*; do
[ -f "$f" ] && printf "%s " $(perl -e 'print((stat("$ARGV[0]"))[9])' -- "$f") && \
printf "%s\0" "$f";
done | tr '\0\n' '\n\0' | sort -k1nr | \
sed "1,20d;s/'/'\"'\"'/g;s/[^ ]* \(.*\)/rm -f -- '\1';/" | tr '\0' '\n' | sh
这是我能够生成的最奇怪的文件名,它甚至可以使用:
touch -- '--a'"'"'b"c
d$e\nf\r!g;h^i`j(k)l*m%n=o?p.txt'
或者尝试“跳出”的文件名:
touch '\'"'"'echo test '"'"'\'
答案3
ls -t1 /mnt/dwh/ftp/dwh_ftp_cbs/ARLOGS/ | tail -n +22 | xargs rm -f
不起作用,因为 的输出ls
将仅包含文件名,而不包含其完整路径。它还会跳过名称以.
.
cd /mnt/dwh/ftp/dwh && ls -At1 | tail -n +21 | xargs rm -f --
可以解决这个问题,但您仍然会遇到包含空格、换行符、撇号、反斜杠或双引号字符的文件名的问题(并且文件名可能包含在语言环境中不形成有效字符的字节)。
(export LC_ALL=C
cd /mnt/dwh/ftp/dwh && ls -At1 |
tail -n +21 |
sed 's/"/\\"/g;s/.*/"&"/' |
xargs rm -f --
)
可以解决其中的大部分问题,但是您仍然会遇到包含换行符的文件名的问题。问题是 的输出ls -At1
不可进行后处理。
的输出ls -Atd1 ./.* ./*
可以进行后处理(因为这些./
前缀指示每个文件名在输出中的起始位置,但并不容易),但是通过传递文件名列表,您可能会面临达到传递给命令的参数数量限制的风险喜欢ls
那样。
最好的办法是使用可以在其全局中进行排序的 shell,或者如果您只需要在 GNU 系统上工作,则依赖 GNU 扩展:
cd /mnt/dwh/ftp/dwh &&
find . ! -name . -prune ! -type d -printf '%T@\t%p\0' |
tr '\0\n' '\n\0' |
sort -rn |
tail -n +21 |
cut -f 2- |
tr '\0\n' '\n\0' |
xargs -r0 rm -f
在这里,我们排除文件类型目录,这会影响编号,但可能更接近您想要的,因为我不认为您想删除目录。如果要删除目录,请删除-type d
并将选项添加-r
到rm
.
或者:
eval "set -- $(COLUMNS=4294967295 ls -Atx --quoting-style=shell-always)"
if [ "$#" -gt 20 ]; then
shift 20
printf '%s\0' "$@" | xargs -r0 rm -f --
fi
或者使用通用编程语言,例如perl
, python
, ruby
...