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

时间:2014-11-05 19:11:08

标签: linux bash ls purge

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

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

ls -t | head -3

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

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

11 个答案:

答案 0 :(得分:38)

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

ls -t | tail -n +4

这将删除这些文件:

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

这也将列出dotfiles:

ls -At | tail -n +4

并使用dotfiles删除:

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

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

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

答案 1 :(得分: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

答案 2 :(得分:6)

ls -t | tail -n +4 | xargs -I {} rm {}

如果你想要1个班轮

答案 3 :(得分:5)

这是ceving和anubhava的回答的组合。 这两种解决方案都不适合我。因为我一直在寻找一个应该每天运行的脚本来备份存档中的文件,所以我想避免ls的问题(有人可能在我的备份保存文件夹中保存了一些有趣的名称文件)。所以我修改了提到的解决方案以满足我的需求。 Ceving的soltution删除了三个最新文件 - 不是我需要和被问到的。

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

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个具有不同时间戳的文件。先运行此操作,然后运行删除代码以测试代码。

答案 4 :(得分:2)

在zsh中:

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

如果要包含点文件,请用(Om[1,-4]D)替换括号部分。

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

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

答案 5 :(得分:2)

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

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

for (int r = 0; r < n; r++)
{
    matrix[r][r] = rand() % 8;
    for (int c = r + 1; c < n; c++)
         matrix[r][c] = matrix[c][r] = rand() % 8;
}

概念证明

#!/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)

答案 6 :(得分:1)

请勿使用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 --

答案 7 :(得分:0)

这使用find代替ls Schwartzian transform

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

find搜索文件并用时间戳装饰它们,并使用制表器分隔这两个值。 sort通过制表器拆分输入并执行常规数字排序,该排序可正确排序浮点数。 tail应该是显而易见的,并cut未完成。

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

答案 8 :(得分:0)

ls -t | tail -n +4 | xargs -I {} rm {}

Michael Ballent的答案最适合

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

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

答案 9 :(得分:0)

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

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

答案 10 :(得分:0)

以下为我工作: (干杯)

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