34

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

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

ls -t | head -3

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

我为此使用 Debian Wheezy 和 bash 脚本。

4

11 回答 11

76

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

ls -t | tail -n +4

这将删除这些文件:

ls -t | tail -n +4 | xargs rm --

这还将列出点文件:

ls -At | tail -n +4

并用点文件删除:

ls -At | tail -n +4 | xargs rm --

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

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

于 2014-11-05T19:17:56.733 回答
26

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

这是ceving 和anubhava 的答案的组合。这两种解决方案都不适合我。因为我正在寻找一个应该每天运行的脚本来备份存档中的文件,所以我想避免出现问题ls(有人可以在我的备份文件夹中保存一些有趣的命名文件)。所以我修改了上述解决方案以满足我的需求。

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

find . -type f -printf '%T@\t%p\n' |
sort -t $'\t' -g | 
head -n -3 | 
cut -d $'\t' -f 2- |
xargs rm

一些解释:

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
)

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

于 2017-12-01T12:06:31.357 回答
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 --

解释这是如何工作的:

  • 查找当前目录中每个文件的发射<mtime> <filename><NUL>
  • sort -g -z基于第一列(次)进行一般(浮点,而不是整数)数字排序,行由 NUL 分隔。
  • read循环中的第一个会while剥离 mtime(完成后不再需要sort)。
  • read循环中的第二个while读取文件名(运行到 NUL)。
  • 循环递增,然后检查一个计数器;如果计数器的状态表明我们已经过了最初的跳过,那么我们打印文件名,由 NUL 分隔。
  • xargs -0然后将该文件名附加到它正在收集以调用的 argv 列表中rm
于 2014-11-05T19:21:43.347 回答
9
ls -t | tail -n +4 | xargs -I {} rm {}

如果你想要一个 1 班轮

于 2014-11-05T19:25:09.017 回答
5

在 zsh 中:

rm /files/to/delete/*(Om[1,-4])

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

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

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

于 2018-09-07T16:56:27.993 回答
3

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

您可以使用所有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 --
于 2017-07-18T12:17:25.967 回答
3
ls -t | tail -n +4 | xargs -I {} rm {}

迈克尔·巴伦特(Michael Ballent)的回答效果最好

ls -t | tail -n +4 | xargs rm --

如果我的文件少于 3 个,则抛出错误

于 2019-02-21T11:04:04.630 回答
2

具有任意数量文件的递归脚本以保留每个目录

还处理带有空格、换行符和其他奇数字符的文件/目录

#!/bin/bash
if (( $# != 2 )); then
  echo "Usage: $0 </path/to/top-level/dir> <num files to keep per dir>"
  exit
fi

while IFS= read -r -d $'\0' dir; do
  # Find the nth oldest file
  nthOldest=$(find "$dir" -maxdepth 1 -type f -printf '%T@\0%p\n' | sort -t '\0' -rg \
    | awk -F '\0' -v num="$2" 'NR==num+1{print $2}')

  if [[ -f "$nthOldest" ]]; then
    find "$dir" -maxdepth 1 -type f ! -newer "$nthOldest" -exec rm {} +
  fi
done < <(find "$1" -type d -print0)

概念证明

$ tree test/
test/
├── sub1
│   ├── sub1_0_days_old.txt
│   ├── sub1_1_days_old.txt
│   ├── sub1_2_days_old.txt
│   ├── sub1_3_days_old.txt
│   └── sub1\ 4\ days\ old\ with\ spaces.txt
├── sub2\ with\ spaces
│   ├── sub2_0_days_old.txt
│   ├── sub2_1_days_old.txt
│   ├── sub2_2_days_old.txt
│   └── sub2\ 3\ days\ old\ with\ spaces.txt
└── tld_0_days_old.txt

2 directories, 10 files
$ ./keepNewest.sh test/ 2
$ tree test/
test/
├── sub1
│   ├── sub1_0_days_old.txt
│   └── sub1_1_days_old.txt
├── sub2\ with\ spaces
│   ├── sub2_0_days_old.txt
│   └── sub2_1_days_old.txt
└── tld_0_days_old.txt

2 directories, 5 files
于 2020-05-05T19:27:34.733 回答
1

作为flohall 答案的扩展。如果要删除除最新的三个文件夹之外的所有文件夹,请使用以下命令:

find . -maxdepth 1 -mindepth 1 -type d -printf '%T@\t%p\n' |
 sort -t $'\t' -g | 
 head -n -3 | 
 cut -d $'\t' -f 2- |
 xargs rm -rf

-mindepth 1忽略父文件夹和-maxdepth 1子文件夹。

于 2020-01-31T09:03:46.983 回答
0

这使用find而不是ls使用Schwartzian 变换

find . -type f -printf '%T@\t%p\n' |
sort -t $'\t' -g |
tail -3 |
cut -d $'\t' -f 2-

find搜索文件并用时间戳装饰它们并使用制表符分隔两个值。sort通过制表符拆分输入并执行一般数字排序,该排序正确地对浮点数进行排序。tail应该是明显的和cut未装饰的。

一般来说,装饰的问题是找到一个合适的分隔符,它不是输入文件名的一部分。此答案使用 NULL 字符。

于 2017-04-26T09:58:01.500 回答
-2

下面为我​​工作:

rm -rf $(ll -t | tail -n +5 | awk '{ print $9}')

于 2020-07-31T09:47:39.410 回答