删除文件夹中除最后(最近)20 个之外的所有文件

删除文件夹中除最后(最近)20 个之外的所有文件

我想删除不同目录中的所有文件,并且只想保留最新的文件 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 -rlrm

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并将选项添加-rrm.

或者:

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...

相关内容