删除bash中最新的X文件

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

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

只是要清楚,只有一个文件存在,它不应该被删除。

现有答案的问题:

  • 无法处理embedded空格或换行符的文件名。
    • 在直接调用rm的解决scheme( rm `...` )的情况下,还会增加无意识通配的风险。
  • 无法区分文件和目录(例如,如果目录是最近修改过的5个文件系统项目中的一个,那么实际上只能保留less于 5个文件,而将rm应用到目录将会失败)。

wnoise的答案解决了这些问题,但是解决scheme是GNU 特有的 (而且非常复杂)。

这是一个符合POSIX标准的实用解决scheme ,只提供一个警告 :它不能处理带有embedded换行符的文件名 – 但我不认为这是大多数人的现实问题。

为了logging,下面解释为什么parsingls输出通常不是一个好主意: http : //mywiki.wooledge.org/ParsingLs

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

以上是低效的 ,因为xargs必须为每个文件名调用一次rm
你的平台的xargs可能允许你解决这个问题:

如果你有GNU xargs ,使用-d '\n' ,这使得xargs把每一个input行看作是一个单独的参数,但是同时传递了许多参数:

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

-r--no-run-if-empty no --no-run-if-empty )确保在没有input的情况下不会调用rm

如果你有BSD xargs (包括在OS X上 ),你可以使用-0来处理NUL分离的input,首先将换行符转换为NUL0x0 )个字符,它也同时传递(通常)所有文件名与GNU xargs ):

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

说明:

  • ls -tp打印文件系统项目的名称,以最近被修改的顺序sorting(最近最先修改的项目)(- -t ),目录打印后跟/标记为( -p )。
  • grep -v '/$'然后通过省略具有尾部//$ )的( -v )行来清除结果列表中的目录。
    • 警告 :由于指向目录符号链接在技​​术上本身不是一个目录,所以这种符号链接不会被排除。
  • tail -n +6跳过列表中的前5个条目,实际上只返回5个最近修改的文件(如果有的话)。
    请注意,为了排除N文件,必须将N+1传递给xargs -n +
  • xargs -I {} rm -- {} (及其变体)然后在所有这些文件上调用rm ; 如果没有匹配, xargs将不会做任何事情。
    • xargs -I {} rm -- {}定义了占位符{} ,它将每个input行整体表示出来 ,因此每个input行会调用一次rm ,但是正确处理embedded空格的文件名。
    • --在任何情况下,确保所有以文件名开头的文件都不会被误认为rm 选项

原始问题的一个变种如果匹配文件需要单独处理或收集在一个shell数组中

 # One by one, in a shell loop (POSIX-compliant): ls -tp | grep -v '/$' | tail -n +6 | while IFS= read -rf; 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 -rf; 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 
 (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个文件(或其他数字)。

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

thelsdj的答案更简单:

 ls -tr | head -n -5 | xargs rm 

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

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

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

 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 

需要为-printffindGNU查找,用于-z的GNUsorting,用于“\ 0”的GNU awk,以及用于-0的GNU xargs,但处理带有embedded换行符或空格的文件。

当前目录中有目录时,所有这些答案都会失败。 这是有用的东西:

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

这个:

  1. 在当前目录中有目录时工作

  2. 试图删除每个文件,即使前一个不能被删除(由于权限等)

  3. 如果当前目录中的文件数量过多,并且xargs通常会将您拧过( -x

  4. 不考虑文件名中的空格(也许你使用的是错误的操作系统?)

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

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

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

忽略新行忽略了安全性和良好的编码。 wnoise有唯一的好答案。 这是他的一个变种,把文件名放在一个数组$ x中

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

如果文件名没有空格,这将工作:

 ls -C1 -t| awk 'NR>5'|xargs rm 

如果文件名有空格,就像

 ls -C1 -t | awk 'NR>5' | sed -e "s/^/rm '/" -e "s/$/'/" | sh 

基本逻辑:

  • 按时间顺序获取文件列表,一列
  • 除了第一个5之外的所有东西(这个例子中n = 5)
  • 第一个版本:发送给rm
  • 第二个版本:创build一个能够正确删除它们的脚本

用zsh

假设你不关心目前的目录,你将不会有超过999个文件(如果你愿意,可以select一个更大的数字,或创build一个while循环)。

 [ 6 -le `ls *(.)|wc -l` ] && rm *(.om[6,999]) 

*(.om[6,999]) , 表示文件, o表示sorting顺序, m表示sorting顺序, m表示修改date(放置a访问时间或者c表示inode更改), [6,999]select一个文件的范围,所以不是rm的第一个。

在Sed-Onliners中find有趣的cmd – 删除最后3行 – find完美的另一种方式皮肤猫(好吧不)但想法:

  #!/bin/bash # sed cmd chng #2 to value file wish to retain cd /opt/depot ls -1 MyMintFiles*.zip > BigList sed -n -e :a -e '1,2!{P;N;D;};N;ba' BigList > DeList for i in `cat DeList` do echo "Deleted $i" rm -f $i #echo "File(s) gonzo " #read junk done exit 0 

我意识到这是一个古老的线索,但也许有人会从中受益。 该命令将查找当前目录中的文件:

 for F in $(find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n' | sort -r -z -n | tail -n+5 | awk '{ print $2; }'); do rm $F; done 

这比之前的一些答案更强大一些,因为它允许将search域限制为匹配expression式的文件。 首先,find符合你想要的任何条件的文件。 打印这些带有时间戳的文件。

 find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n' 

接下来,按照时间戳对它们进行sorting:

 sort -r -z -n 

然后,从列表中敲掉4个最新的文件:

 tail -n+5 

抓住第二列(文件名,而不是时间戳):

 awk '{ print $2; }' 

然后把整个东西包装成一个for语句:

 for F in $(); do rm $F; done 

这可能是一个更详细的命令,但我有更好的运气能够针对条件文件和执行更复杂的命令对他们。

 leaveCount=5 fileCount=$(ls -1 *.log | wc -l) tailCount=$((fileCount - leaveCount)) # avoid negative tail argument [[ $tailCount < 0 ]] && tailCount=0 ls -t *.log | tail -$tailCount | xargs rm -f 

我把它做成了一个bash shell脚本。 用法: keep NUM DIR ,其中NUM是要保留的文件数,DIR是要擦除的目录。

 #!/bin/bash # Keep last N files by date. # Usage: keep NUMBER DIRECTORY echo "" if [ $# -lt 2 ]; then echo "Usage: $0 NUMFILES DIR" echo "Keep last N newest files." exit 1 fi if [ ! -e $2 ]; then echo "ERROR: directory '$1' does not exist" exit 1 fi if [ ! -d $2 ]; then echo "ERROR: '$1' is not a directory" exit 1 fi pushd $2 > /dev/null ls -tp | grep -v '/' | tail -n +"$1" | xargs -I {} rm -- {} popd > /dev/null echo "Done. Kept $1 most recent files in $2." ls $2|wc -l 

删除除了10个最新(最新)文件以外的所有文件

 ls -t1 | head -n $(echo $(ls -1 | wc -l) - 10 | bc) | xargs rm 

如果less于10个文件没有文件被删除,你将有:错误头:非法行数 – 0

用bash计数文件

在Debian上运行(假设我在其他发行版上得到相同的结果:rm:无法删除目录`..'

这很烦人..

无论如何,我调整了上面,并添加grep的命令。 在我的情况下,我有一个目录中的6个备份文件,例如file1.tar file2.tar file3.tar等,我想只删除最旧的文件(在我的情况下删除第一个文件)

我运行删除最旧的文件的脚本是:

ls -C1 -t | grep文件| awk'NR> 5'| xargs rm

这(如上)删除我的第一个文件,例如file1.tar,这也留下了file2 file3 file4 file5和file6