删除bash脚本中除最新3之外的所有文件

byt*_*e77 16 linux bash ls purge

问题:除了最新的3之外,如何删除目录中的所有文件?

查找最新的3个文件很简单:

ls -t | head -3
Run Code Online (Sandbox Code Playgroud)

但我需要找到除最新的3个文件以外的所有文件.我该怎么做,如何在同一行中删除这些文件而不为其做一个不必要的for循环?

我正在使用Debian Wheezy和bash脚本.

les*_*ana 43

这将列出除最新三个以外的所有文件:

ls -t | tail -n +4
Run Code Online (Sandbox Code Playgroud)

这将删除这些文件:

ls -t | tail -n +4 | xargs rm --
Run Code Online (Sandbox Code Playgroud)

这也将列出dotfiles:

ls -At | tail -n +4
Run Code Online (Sandbox Code Playgroud)

并使用dotfiles删除:

ls -At | tail -n +4 | xargs rm --
Run Code Online (Sandbox Code Playgroud)

但要注意:ls当文件名包含有趣的字符(如换行符或空格)时,解析可能会很危险.如果您确定您的文件名不包含有趣的字符,那么解析ls是非常安全的,如果它只是一次性脚本则更是如此.

如果您正在开发一个重复使用的脚本,那么您肯定不会解析输出ls并使用此处描述的方法:http: //mywiki.wooledge.org/ParsingLs

  • @DevilsChild我确实看到删除TB的备份因为缓冲区溢出在其名称中创建了一个带有垃圾的文件,并且有人认为因为文件名创建是程序化的,所以不会发生异常名称.走捷径可能会让你苦苦挣扎. (5认同)
  • rm命令中双破折号-的作用是什么? (2认同)
  • 这可以防止文件名以破折号或减号开头。http://unix.stackexchange.com/questions/1519/how-do-i-delete-a-file-whose-name-begins-with-hyphen-aka-dash-or-minus (2认同)

Cha*_*ffy 9

以下看起来有点复杂,但即使有异常或故意恶意的文件名,也要非常谨慎.不幸的是,它需要GNU工具:

count=0
while IFS= read -r -d ' ' && IFS= read -r -d '' filename; do
  (( ++count > 3 )) && printf '%s\0' "$filename"
done < <(find . -maxdepth 1 -type f -printf '%T@ %P\0' | sort -g -z) \
     | xargs -0 rm -f --
Run Code Online (Sandbox Code Playgroud)

解释这是如何工作的:

  • 查找<mtime> <filename><NUL>当前目录中每个文件的发出.
  • sort -g -z 基于第一列(时间)进行一般(浮点,而不是整数)数字排序,其中行由NUL分隔.
  • 第一readwhile环带断修改时间(不再需要后sort完成).
  • 第二readwhile循环读取的文件名(运行,直到NUL).
  • 循环递增,然后检查计数器; 如果计数器的状态表明我们已经过了最初的跳过,那么我们打印文件名,由NUL分隔.
  • xargs -0然后将该文件名附加到它正在收集以调用的argv列表中rm.

  • 你错过了 xargs 中的“-0”吗?另外,您可以使用 group 和 dummy read 跳过前三个:`{ read; 读; 读; 当……完成时;} &lt; &lt;(find ...)` 这将避免需要计数器。 (2认同)
  • @gniourf_gniourf,唯一的原因是为了避免外部命令调用的次数,每次调用`rm`一次MAX_ARGV填充比每次调用一次更快.对缺少的'IFS =`有好处. (2认同)
  • “不起作用”有点强——一般用例是亚秒级精度无关紧要(在许多文件系统上,甚至在输入数据中都不存在亚秒级精度)——但是由于我们已经依赖于 GNU 工具,因此更改没有任何危害。 (2认同)

Mic*_*ent 6

ls -t | tail -n +4 | xargs -I {} rm {}
Run Code Online (Sandbox Code Playgroud)

如果你想要1个衬垫


flo*_*all 6

“ ls”(奇怪的命名文件)没有问题的解决方案

这是ceving和anubhava答案的结合。两种解决方案都不适合我。因为我一直在寻找一个每天都要运行的脚本来备份存档中的文件,所以我想避免出现问题ls(有人可以在备份文件夹中保存一些有趣的命名文件)。因此,我修改了上述解决方案以适应我的需求。Ceving的解决方案删除了​​三个最新文件,而不是我需要的文件和被询问的文件。

我的解决方案将删除所有文件,最新的三个文件除外

find . -type f -printf '%T@\t%p\n' |
sort -t $'\t' -g | 
head -n -3 | 
cut -d $'\t' -f 2- |
xargs rm
Run Code Online (Sandbox Code Playgroud)

一些解释:

find列出当前文件夹中的所有文件(不是目录)。它们带有时间戳打印。
sort根据时间戳对行进行排序(最旧的排在最前面)。
head打印出前行,直到最后3行。
cut删除时间戳。为每个选定的文件
xargs运行rm

供您验证我的解决方案:

(
touch -d "6 days ago" test_6_days_old
touch -d "7 days ago" test_7_days_old
touch -d "8 days ago" test_8_days_old
touch -d "9 days ago" test_9_days_old
touch -d "10 days ago" test_10_days_old
)
Run Code Online (Sandbox Code Playgroud)

这将创建5个文件,在当前文件夹不同的时间戳。首先运行此脚本,然后删除旧文件的代码。

  • 我喜欢这个脚本,但是当过滤器没有捕获任何文件时会发生什么?在我有限的测试中,“rm”以非零值退出。作为测试,运行上面的文件创建,然后运行脚本,但在“head”命令中使用值“-8”。这不会返回任何值,这会使 rm 退出并出现错误。然而,即使没有结果,“rm”上的“-f”标志似乎也会使其以 0 退出。因此,如果您需要脚本在没有结果的情况下干净退出,则可以将最后一行修改为“xargs rm -f”。 (2认同)

anu*_*ava 5

不要使用ls -t,因为它对于可能包含空格或特殊全局字符的文件名是不安全的。

您可以使用所有gnu基于的实用程序来删除当前目录中除 3 个最新文件之外的所有文件:

find . -maxdepth 1 -type f -printf '%T@\t%p\0' |
sort -z -nrk1 |
tail -z -n +4 |
cut -z -f2- |
xargs -0 rm -f --
Run Code Online (Sandbox Code Playgroud)


Fun*_*lad 5

在 zsh 中:

rm /files/to/delete/*(Om[1,-4])
Run Code Online (Sandbox Code Playgroud)

如果要包含点文件,请将括号中的部分替换为(Om[1,-4]D).

我认为这适用于文件名中的任意字符(只是用换行符检查)。

说明:括号包含 Glob 限定符。O表示“排序,降序”,m表示 mtime(有关man zshexpn其他排序键,请参见大型联机帮助页;搜索“被排序”)。[1,-4]仅返回基于 1 的索引 1 到 (last + 1 - 4) 的匹配项(注意-4删除除 3 之外的所有项)。