当前位置:  开发笔记 > 编程语言 > 正文

删除bash中除最新的X文件之外的所有文件

如何解决《删除bash中除最新的X文件之外的所有文件》经验,为你挑选了8个好方法。

有一个简单的方法,在一个非常标准的UNIX环境中使用bash运行命令来删除目录中除最新的X文件之外的所有文件吗?

为了给出一个具体的例子,想象一下一些cron作业每小时写一个文件(比如一个日志文件或一个tar-up up备份)到一个目录.我想要一种方法来运行另一个cron作业,它将删除该目录中最旧的文件,直到少于5个.

而且要清楚,只有一个文件存在,它永远不应该被删除.



1> mklement0..:

现有答案存在的问题:

无法处理带有嵌入空格或换行符的文件名.

对于rm直接在不带引号的命令substitution(rm `...`)上调用的解决方案,会增加意外通配的风险.

无法区分文件和目录(即,如果目录恰好是最近修改的5个文件系统项目之一,那么您实际上将保留少于 5个文件,并且应用于rm目录将失败).

wnoise的答案解决了这些问题,但解决方案是GNU特定的(并且非常复杂).

这是一个实用的,符合POSIX标准的解决方案,只有一个警告:它无法处理带有嵌入式换行符的文件名- 但我不认为这是大多数人的现实问题.

为了记录,这里解释为什么解析ls输出通常不是一个好主意:http://mywiki.wooledge.org/ParsingLs

ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}

上面的效率很低,因为xargs必须rm每个文件名调用一次.
您的平台xargs可能允许您解决此问题:

如果你有GNU xargs,使用-d '\n',这使得xargs考虑每个输入线路分离的说法,但经过许多参数作为将适合在命令行上一次:

ls -tp | grep -v '/$' | tail -n +6 | xargs -d '\n' -r rm --

-r(--no-run-if-empty)确保rm在没有输入的情况下不调用.

如果你有BSD xargs(包括OS X),你可以使用-0处理NUL-分隔输入,经过第一平移换行至NUL(0x0)字符,这也传递(典型值)的所有文件名.在一次(也将与GNU工作xargs):

ls -tp | grep -v '/$' | tail -n +6 | tr '\n' '\0' | xargs -0 rm --

说明:

ls -tp打印文件系统项目的名称,按照最近修改的顺序排序,按降序排列(最近修改的项目首先)(-t),目录打印有尾部/标记为(-p).

grep -v '/$'然后通过省略(-v)具有尾随/(/$)的行来从结果列表中清除目录.

警告:由于指向目录符号链接在技​​术上本身不是目录,因此不会排除此类符号链接.

tail -n +6跳过列表中的前5个条目,实际上返回除了最近修改的5个文件之外的所有文件(如果有的话).
请注意,为了排除N文件,N+1必须传递给tail -n +.

xargs -I {} rm -- {}(及其变体)然后调用rm所有这些文件; 如果根本没有比赛,xargs将不会做任何事情.

xargs -I {} rm -- {}定义占位符{},表示每个输入行作为一个整体,因此rm然后为每个输入行调用一次,但具有正确处理嵌入空格的文件名.

--在任何情况下确保了发生在开始任何文件名-是不误选项通过rm.


变化上的原始问题,在情况下,匹配的文件需要被处理单独收集在壳阵列:

# One by one, in a shell loop (POSIX-compliant):
ls -tp | grep -v '/$' | tail -n +6 | while IFS= read -r f; do echo "$f"; done

# One by one, but using a Bash process substitution (<(...), 
# so that the variables inside the `while` loop remain in scope:
while IFS= read -r f; do echo "$f"; done < <(ls -tp | grep -v '/$' | tail -n +6)

# Collecting the matches in a Bash *array*:
IFS=$'\n' read -d '' -ra files  < <(ls -tp | grep -v '/$' | tail -n +6)
printf '%s\n' "${files[@]}" # print array elements


当然比这里的大多数其他答案要好,所以我很乐意提供支持,即使因为我认为忽略换行案例只是谨慎行事.

2> Espo..:

删除目录中除最新文件的5个(或任何数量)之外的所有文件.

rm `ls -t | awk 'NR>5'`


简洁可读,也许,但使用起来很危险; 如果试图删除用`touch'hello*world'`创建的文件,这将删除**绝对当前目录中的所有内容**.
`ls -t | awk'NR> 5'| xargs rm -f`如果你喜欢管道,你需要在没有任何东西要删除的情况下抑制错误.
我需要这个只考虑我的档案文件.将`ls -t`改为`ls -td*.bz2`
我通过将其更改为rm -rf`ls -t |来将其用于目录 awk'NR> 1'`(我只想要最近的).谢谢!

3> thelsdj..:
(ls -t|head -n 5;ls)|sort|uniq -u|xargs rm

此版本支持带空格的名称:

(ls -t|head -n 5;ls)|sort|uniq -u|sed -e 's,.*,"&",g'|xargs rm


此命令将无法正确处理名称中包含空格的文件.
如果你有5个或更少,这将删除你的所有文件!将`--no-run-if-empty`添加到`xargs`中,如`(ls -t | head -n 5; ls)| sort | uniq -u | xargs --no-run-if-empty rm`请更新答案.
`(ls -t | head -n 5; ls)`是[命令组](http://tldp.org/LDP/abs/html/special-chars.html#PARENSREF).它打印两次最近的5个文件.`sort`将相同的行放在一起.`uniq -u`删除重复项,以便除了5个最新文件之外的所有文件都保留.`xargs rm`在每个上面调用`rm`.
即使是"支持带空格的名字"也是危险的.考虑一个包含文字引号的名称:`touch'foo"bar'`将抛弃整个命令的其余部分.
...使用`xargs -d $'\n''比在内容中插入引号更安全,虽然NUL分隔输入流(需要使用除了'ls`以外的其他东西*真正*做正确)理想的选择.

4> Fabien..:

更简单的thelsdj答案:

ls -tr | head -n -5 | xargs --no-run-if-empty rm 

ls -tr显示所有文件,最早的文件(-t最新的第一个,-r反向).

head -n -5显示除最后5行之外的所有行(即5个最新文件).

xargs rm为每个选定的文件调用rm.


需要在xargs中添加--no-run-if-empty,以便在少于5个文件时不会失败.
@AlJoslin,`-1`是输出到管道时的默认值,因此这里不是必需的.这有很大的问题,与使用空格,引号和c解析名称时`xargs`的默认行为有关.

5> wnoise..:
find . -maxdepth 1 -type f -printf '%T@ %p\0' | sort -r -z -n | awk 'BEGIN { RS="\0"; ORS="\0"; FS="" } NR > 5 { sub("^[0-9]*(.[0-9]*)? ", ""); print }' | xargs -0 rm -f

需要GNU查找-printf,GNU排序为-z,GNU awk表示"\ 0",GNU xargs表示-0,但处理带有嵌入换行符或空格的文件.


如果要删除目录,只需将-f更改为-d并将-r添加到rm.找 .-maxdepth 1-type d -printf'%T @%p\0'| sort -r -z -n | awk'BEGIN {RS ="\ 0"; ORS = "\ 0"; FS =""} NR> 5 {sub("^ [0-9]*(.[0-9]*)?",""); print}'| xargs -0 rm -rf

6> 小智..:

当前目录中有目录时,所有这些答案都会失败.这是有效的:

find . -maxdepth 1 -type f | xargs -x ls -t | awk 'NR>5' | xargs -L1 rm

这个:

    当前目录中有目录时有效

    尝试删除每个文件,即使前一个文件无法删除(由于权限等)

    失败时的文件在当前目录数量过多安全,xargs通常会去你的过(的-x)

    不适合文件名中的空格(也许你使用的是错误的操作系统?)


如果`find`返回的文件名多于可以在单个命令行上传递给`ls -t`的文件名,会发生什么?(提示:你得到多次运行`ls -t`,每个运行只是单独排序,而不是具有全局正确的排序顺序;因此,当运行足够大的目录时,这个答案会严重破坏).

7> Mark..:
ls -tQ | tail -n+4 | xargs rm

按修改时间列出文件名,引用每个文件名.排除前3位(最近3位).删除剩余的.

在mklement0的有用评论之后编辑(谢谢!):更正了-n + 3参数,并注意如果文件名包含换行符和/或目录包含子目录,这将无法按预期工作.


嗯,该选项已经在GNU核心工具中使用了大约20年,但在BSD变体中没有提到.你在Mac上吗?
@Mark:++表示`-Q`.是的,`-Q`是GNU扩展(这里是[POSIX`ls`规范](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html)).一个小警告(在实践中很少出现问题):`-Q`将文件名中的嵌入_newlines_编码为文字`\n`,其中`rm`将无法识别.要排除第一个_3_,`xargs`参数必须为'+ 4`.最后,一个警告也适用于大多数其他答案:如果当前目录中没有_subdirectories_,则命令将仅按预期工作.

8> Ian Kelling..:

忽略换行符会忽略安全性和良好的编码.wnoise有唯一的好答案.这是他的一个变体,它将文件名放在数组$ x中

while IFS= read -rd ''; do 
    x+=("${REPLY#* }"); 
done < <(find . -maxdepth 1 -printf '%T@ %p\0' | sort -r -z -n )


我建议清除`IFS` - 否则你可能会丢失文件名中的尾随空格.可以将其范围扩展到read命令:`IFS = read -rd''; do`
推荐阅读
mobiledu2402852413
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有