Linux 删除除bash脚本中最新的3个文件以外的所有文件

Linux 删除除bash脚本中最新的3个文件以外的所有文件,linux,bash,ls,purge,Linux,Bash,Ls,Purge,问题:如何删除目录中除最新3个文件以外的所有文件 查找最新的3个文件很简单: ls -t | head -3 但我需要找到除最新的3个文件以外的所有文件。如何做到这一点,以及如何删除同一行中的这些文件,而不为此创建不必要的for循环 我正在为此使用Debian Wheezy和bash脚本。这将列出除最新的三个文件之外的所有文件: ls -t | tail -n +4 这将删除以下文件: ls -t | tail -n +4 | xargs rm -- 这还将列出点文件: ls -At |

问题:如何删除目录中除最新3个文件以外的所有文件

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

ls -t | head -3
但我需要找到除最新的3个文件以外的所有文件。如何做到这一点,以及如何删除同一行中的这些文件,而不为此创建不必要的for循环


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

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

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 -At | tail -n +4
ls -At | tail -n +4 | xargs rm --
但是要小心:当文件名包含有趣的字符,如换行符或空格时,解析
ls
可能会很危险。如果您确定您的文件名不包含有趣的字符,那么解析
ls
是非常安全的,如果它是一次性脚本,则更是如此


如果您正在开发一个供重复使用的脚本,那么您肯定不应该解析
ls
的输出,而应该使用此处描述的方法:

以下内容看起来有点复杂,但要非常谨慎,即使是不寻常的或故意恶意的文件名,也要确保正确。不幸的是,它需要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 --
count=0
而IFS=read-r-d''&&IFS=read-r-d''文件名;做
((++count>3))&&printf“%s\0'$filename”
完成<

如果您想要一个1行程序

,则使用
查找
,而不是带有

find
搜索文件并用时间戳装饰它们,并使用制表器分隔这两个值<代码>排序
通过制表器拆分输入,并执行常规数字排序,从而对浮点数进行正确排序<代码>尾部应该是明显的,
切割
去除装饰


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

不要使用
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 --
使用“ls”(命名奇怪的文件)没有问题的解决方案 这是塞文和阿努巴瓦答案的结合。 这两种解决方案对我都不起作用。因为我在寻找一个应该每天运行的脚本来备份存档中的文件,所以我想避免出现
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个具有不同时间戳的文件。首先运行此脚本,然后运行用于删除旧文件的代码。

在zsh中:

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

我认为这可以正确地处理文件名中的任意字符(只是用换行符检查)

说明:括号中包含全局限定符
O
表示“排序依据,降序”,
m
表示mtime(请参见
man zshexpn
了解其他排序键-大型手册页;搜索“被排序”)
[1,-4]
只返回一个索引1到(最后+1-4)的匹配项(注意
-4
用于删除除3之外的所有索引)

迈克尔·巴伦特的答案最适用于

ls -t | tail -n +4 | xargs rm --
如果作为扩展名的文件少于3个,则抛出错误。如果要删除除最新的三个文件夹之外的所有文件夹,请使用以下命令:

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
子文件夹。

递归脚本,每个目录保留任意数量的文件 还处理带有空格、换行符和其他奇数字符的文件/目录
下面的对我有用:(Cheers
ls
实际上是用于此工作的错误工具--请参阅。如果您有GNU find,您可以使用带有时间戳的
-printf
格式字符串做得更好(对于
排序-n-z
,理想情况下在UNIX时间),一个分隔符,然后是一个NUL;这样,即使是带换行符的文件名也不会被忽略。我也不同意在这里使用循环是不必要的。正确、稳健地做事情与简洁地做事情不同,但其他任何事情都是……嗯……不正确的。@DevilsChild:取决于你是否关心正确。如果你不这样做,你可以例如,只需将管道连接到xargs…但如果有什么重要的事情(如备份脚本),请不要这样做@DevilsChild我确实看到过TB的备份被删除,因为缓冲区溢出创建了一个名称中包含垃圾的文件,而且有人认为,由于文件名创建是程序性的,所以不可能出现不寻常的名称。走捷径会让你痛苦不堪。在
rm
co中使用双破折号
-
的目的是什么mmand?这是一种防止文件名以破折号或负号开头的保护措施。非常优雅的解决方案:)您是否错过了xargs中的
-0
?另外,您可以通过使用group和dummy read跳过前三个:
{read;read;read;while…done;}<@gniourf\u gniourf,是的,但是它们必须是带有
-d''
的dummy reads,这使得它们足够长,我可以使用计数器。对
-0
的捕捉很好;我只是在这一点之前进行了测试。
IFS=''read-r-d''stamp filename
能代替(单独的)伪读吗?@gniourf\u gniourf,o
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
#!/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