如何删除给定目录中最旧的目录?

AAa*_*Aaa 10 scripting bash timestamps files

可能的重复:
用于移动最旧文件的 Shell 脚本?

我有一个备份目录,用于存储x需要备份的其他目录。我需要在另一个目录移动到备份之前运行的东西,它会检查目录数量x是否达到,如果达到,它将删除最旧的目录。

它应该在bash脚本中完成。

Sor*_*gal 20

解析 的输出ls不可靠的

相反,用于find定位目录并按sort时间戳对其进行排序。例如:

IFS= read -r -d $'\0' line < <(find . -maxdepth 1 -type d -printf '%T@ %p\0' \ 
    2>/dev/null | sort -z -n)
file="${line#* }"
# do something with $file here
Run Code Online (Sandbox Code Playgroud)

这一切在做什么?

首先,find命令在当前目录(.)中定位所有目录,但不在当前目录(-maxdepth 1)的子目录中,然后打印出:

  • 时间戳
  • 空间
  • 文件的相对路径
  • 一个空字符

时间戳很重要。的%T@格式说明-printf符分解为T,表示文件的“上次修改时间”(mtime)和@,表示“自 1970 年以来的秒数”,包括小数秒。

空格只是一个任意的分隔符。文件的完整路径是为了我们以后可以引用它,NULL 字符是一个终止符,因为它是文件名中的非法字符,因此让我们确信我们到达了路径的末尾文件。

我已包含,2>/dev/null以便排除用户无权访问的文件,但会抑制有关它们被排除的错误消息。

find命令的结果是当前目录中所有目录的列表。该列表通过管道传送到sort其被指示:

  • -z 将 NULL 视为行终止符而不是换行符。
  • -n 按数字排序

由于自 1970 年以来的秒数总是上升,我们想要时间戳是最小数字的文件。第一个结果sort将是包含最小编号时间戳的行。剩下的就是提取文件名。

find,sort管道的结果通过进程替换传递到read,在那里它被读取,就好像它是标准输入上的文件一样。

read我们将IFS变量设置为空的上下文中,这意味着空格不会被不恰当地解释为分隔符。read被告知-r,它禁用了转义扩展,而-d $'\0',它使行尾分隔符为 NULL,与我们的find,sort管道的输出相匹配。

第一个数据块,代表最旧的目录路径,前面有它的时间戳和一个空格,被读入变量line。接下来,将参数替换与表达式 一起使用#*,它只是将字符串开头到第一个空格(包括空格)的所有字符替换为空。这会去除修改时间戳,只留下文件的完整路径。

此时文件名已存储在其中$file,您可以对它进行任何您喜欢的操作,包括rm -rf "$file".

没有更简单的方法吗?

不。更简单的方法有问题。

如果你使用ls -t和管道到tail你会打破文件名中带有换行符的文件。如果您rm $(anything)然后名称中带有空格的文件将导致损坏。如果您rm "$(anything)"然后名称中带有尾随换行符的文件将导致损坏。

也许在特定情况下,您肯定知道一种更简单的方法就足够了,但是如果可以避免的话,您永远不应该在脚本中编写这样的假设。

编辑

#!/usr/bin/env bash

dir="$1"
min_dirs=3

[[ $(find "$dir" -maxdepth 1 -type d | wc -l) -ge $min_dirs ]] &&
IFS= read -r -d $'\0' line < <(find "$dir" -maxdepth 1 -printf '%T@ %p\0' 2>/dev/null | sort -z -n)
file="${line#* }"
ls -lLd "$file"
Run Code Online (Sandbox Code Playgroud)

一个更完整的问题解决方案,因为它首先检查目录计数。