如何在Linux中删除除10个最新文件以外的所有文件?


47

我正在尝试保持一个充满日志文件的目录可管理。每晚,我想删除除最近的10个以外的所有内容。我怎样才能在一个命令中执行此操作?


3
看看logrotate
knittl 2011年


@michael如果我的讨论是先创建的,我的怎么能成为那个副本?
dev4life

@ovaherenow“你的”?我在任何一个问题上都没有看到你的名字,所以我不确定你的意思。我的评论甚至没有说明哪一个与另一个重复。但这并不重要 - 它只是一个评论,带有链接。它可以添加到问题或两个问题中。这取决于其他人 - 管理员 - 将一个或另一个标记为重复。没有邪恶的诡计,也不应该被察觉。比第一个问题更重要的是哪个问题有最好的答案 - 再次,链接促进了这个讨论,它没有确定答案。
迈克尔

@michael哇。这真的很奇怪。这个帖子是我打开的,但显然不是我的用户名。但我收到有关此主题的通知。
dev4life

Answers:


57

对于便携可靠的解决方案,请尝试以下方法:

ls -1tr | head -n -10 | xargs -d '\n' rm -f --

其中tail -n -10一个答案中的语法似乎无处不在(即,不在我的RHEL5系统上)。

使用$()``在命令行上rm运行的风险

  1. 用空格分割文件名,和
  2. 超过最大命令行字符限制。

xargs修复了这两个问题,因为它会自动计算出它在字符数限制内可以传递多少个args,并且-d '\n'它只会在输入的行边界处分割。从技术上讲,这仍然会导致文件名中出现换行问题,但这远远不如带有空格的文件名常见,并且围绕换行的唯一方法就是复杂得多,可能至少涉及awk,如果不是perl。

如果您没有xargs(可能是旧的AIX系统?),您可以将其设为循环:

ls -1tr | head -n -10 | while IFS= read -r f; do
  rm -f "$f"
done

这会有点慢,因为它会rm为每个文件生成一个单独的文件,但仍然会避免上面的警告1和2(但仍然会受到文件名中的换行符的影响)。


@HaukeLaging:从head(1)手册页:-n, --lines=[-]K print the first K lines instead of the first 10; with the leading '-', print all but the last K lines of each file
Isaac Freeman

在Solaris 10上headxargs给出错误。正确的选择是head -n 10xargs rm -f。完全命令是ls -1tr ./ | head -n 10 | xargs rm -rf
DmitrySandalov 2014年

1
注意ls -1tr是数字ONE而不是字母“L”。
shonky linux用户2015年

1
还应该注意,换行符是ext文件名中罕见但有效的字符。这意味着如果你有“那个”和“那个”的文件,如果这个脚本点击“那个”,那么它将删除“那个”,当它无法删除“的东西”时出错,并离开“那个\ n”(预定目标)不受影响。
Synthead 2016年

1
@IsaacFreeman我删除了我的坏建议,欢呼。
Walf

20

您希望包含在脚本中的代码是

 rm -f $(ls -1t /path/to/your/logs/ | tail -n +11)

-1(数字1)选项打印在一行上的每个文件,是安全的。告诉它忽略不存在的文件什么时候什么都不返回的-f选项。rmls


原创海报,在这里。我尝试了这个命令,但它不起作用 - 我得到错误“rm:missing operand”
user75814

2
你使用了正确的路径吗?显然/path/to/your/logs只是为了让你进入正确的道路。要找到错误执行部件ls -t /path/...ls -t /path/... | tail -n +11单独检查结果是否正确。
DanielBöhmer

1
@HaukeLaging我很确定你错了。介意+之前的数字。它tail不会打印最后n个元素,而是从n个开始的所有元素。我再次签入,命令肯定应该删除所有情况下只有10个最新文件的元素。
DanielBöhmer13年

@halo确实,抱歉。
Hauke Laging 2013年

2
我认为你的答案是非常危险的ls打印文件名,而不是路径所以你rm -f会尝试删除当前目录中的文件
JürgenSteinblock17年6

13

显然解析ls是邪恶的

如果每天都创建每个文件,并且您希望保留在过去10天内创建的文件,则可以执行以下操作:

find /path/to/files -mtime 10 -delete

或者,如果每个文件是任意创建的:

find /path/to/files -maxdepth 1 -type f -printf '%Ts\t%P\n' | sort -n | head -n -10 | cut -f 2- | xargs rm -rf

4
-mtime 10 -delete仅删除10天前修改过的文件。旧文件不会被删除。-mtime +11 -delete将删除11天或更久前修改过的文件,留下最后10天的日志文件。
马库斯

1
我觉得用的%p,而不是%P需要的printf,这样的文件得到了具有完整路径。否则rm -rf不起作用。
jalopaba

9

像logrotate这样的工具可以帮到你。它使日志管理更容易。你还可以包括额外的清理程序,就像晕晕一样。



1

这里

#! /bin/sh
# keepnewest
#
# Simple directory trimming tool to handle housekeeping
# Scans a directory and deletes all but the N newest files
#
# Usage: cleanup <dir> <number of files to keep>
#
# v 1.0 Piers Goodhew 1/mar/2007. No rights retained.


if [ $# -ne 2 ]; then
  echo 1>&2 "Usage: $0 <dir> <number of files to keep>"
  exit 1
fi

cd $1
files_in_dir=`ls | wc -l`
files_to_delete=`expr $files_in_dir - $2`
if [ $files_to_delete -gt 0 ]; then
  ls -t | tail -n $files_to_delete | xargs rm
  if [ $? -ne 0 ]; then
    echo "An error ocurred deleting the files"
    exit 1
  else
    echo "$files_to_delete file(s) deleted."
  fi
else
  echo "nothing to delete!"
fi

1
谢谢你提供这个答案。虽然提供这样的代码很有帮助,但它也有助于提供有关如何使用它或代码的功能的一些附加信息。您可以使用编辑按钮对您的帖子进行进一步的改进。
nhinkle

1

在Bash中你可以尝试:

ls -1t | (i=0; while read f; do
  if [ $i -lt 10 ]; then
    ((i++))
    continue
  else
    rm -f "$f"
  fi
done)

这跳过10个最新的als删除其余的。logrotate可能更好,我只是想纠正错误的shell相关答案。


1

不确定这会对任何人有所帮助,只是为了避免我刚刚用于ls执行排序但是然后使用文件inode进行删除的不稳定字符的潜在问题。

对于当前目录,这将根据修改时间保留最新的10个文件。

ls -1tri | awk '{print $1}' | head -n -10 | xargs -n1 -t -Iinode find . -inum inode -exec rm -i {} \;


0

我需要一个优雅的busybox(路由器)解决方案,所有的xargs或阵列解决方案对我来说都没用 - 没有这样的命令。find和mtime不是正确的答案,因为我们正在谈论10个项目,而不一定是10天。Espo的答案是最短,最干净,也可能是最不可逆的答案。

空格出错以及没有要删除的文件都是以标准方式解决的:

rm "$(ls -td *.tar | awk 'NR>7')" 2>&-

更多教育版:如果我们使用awk的话,我们可以做到这一切。通常,我使用此方法将变量从awk传递(返回)到sh。当我们阅读所有无法完成的时间时,我不同意:这是方法。

.tar文件的示例,文件名中的空格没有问题。要测试,请将“rm”替换为“ls”。

eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}')

说明:

ls -td *.tar列出按时间排序的所有.tar文件。要应用于当前文件夹中的所有文件,请删除“d * .tar”部分

awk 'NR>7... 跳过前7行

print "rm \"" $0 "\"" 构造一行:rm“文件名”

eval 执行它

由于我们正在使用rm,我不会在脚本中使用上面的命令!更明智的用法是:

(cd /FolderToDeleteWithin && eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}'))

在使用ls -t命令的情况下,不会对如下愚蠢的例子造成任何伤害:touch 'foo " bar'touch 'hello * world'。并非我们在现实生活中创建具有此类名称的文件!

边注。如果我们想以这种方式将变量传递给sh,我们只需修改print:

print "VarName="$1

将变量VarName设置为值$1。可以一次创建多个变量。这VarName成为一个普通的sh变量,之后通常可以在脚本或shell中使用。因此,使用awk创建变量并将它们返回给shell:

eval $(ls -td *.tar | awk 'NR>7 { print "VarName="$1 }'); echo "$VarName"

0

我同意解析ls是邪恶的!

确保只有最后5个文件保留在/srv/backups路径中。

find /srv/backups -maxdepth 1 -type f -printf '%Ts\t%P\n' \
    | sort -rn \
    | tail -n +6 \
    | cut -f2- \
    | xargs -r r

0

基于Isaac Freeman的响应,但在任何目录上工作:

ls -1tr $PATH_TO_DELETE/* | head -n -10 | xargs -d '\n' rm -f --

你也可以设置一个前缀,比如 $PATH_TO_DELETE/testFi*

如果你使用*PATH ls会输出绝对文件名,至少在“我的”bash :)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.