可能的重复:
用于移动最旧文件的 shell 脚本?
我有一个备份目录,存放x
其他需要备份的目录。我需要在另一个目录移动到备份之前运行的东西,它将检查目录数量是否达到x
,如果达到,它将删除最旧的目录。
它应该在脚本中完成bash
。
答案1
ls
解析is的输出不可靠的。
相反,使用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
这一切到底是在做什么?
首先,这些find
命令查找当前目录 ( .
) 中的所有目录,但不查找当前目录 ( -maxdepth 1
) 的子目录中的所有目录,然后打印出:
- 时间戳
- 空间
- 文件的相对路径
- 空字符
时间戳很重要。的格式%T@
说明-printf
符分为T
,表示文件的“上次修改时间”(mtime) 和@
,表示“自 1970 年以来的秒数”,包括小数秒。
空格只是一个任意的分隔符。文件的完整路径是为了方便我们稍后引用它,而 NULL 字符是终止符,因为它是文件名中的非法字符,因此可以让我们确定已到达文件路径的末尾。文件。
我已包含在内2>/dev/null
,以便排除用户无权访问的文件,但会抑制有关它们被排除的错误消息。
该命令的结果find
是当前目录中所有目录的列表。该列表通过管道传送到sort
,指示:
-z
将 NULL 视为行终止符而不是换行符。-n
按数字排序
由于 1970 年以来的秒数总是增加,因此我们需要时间戳是最小数字的文件。第一个结果sort
将是包含最小编号时间戳的行。剩下的就是提取文件名。
find
,管道的结果sort
通过流程替代to 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"
问题的更完整解决方案,因为它首先检查目录计数。
答案2
你可以使用类似下面的东西:
#!/bin/sh
keep=3
while [ `ls -1 | wc -l` -gt $keep ]; do
oldest=`ls -c1 | head -1`
echo "remove $oldest"
rm -rf $oldest
done
使用 but 可能会find . -type d -maxdepth 1
更好ls
。这取决于您用于目录的命名模式。如果它们自然地按名称正确排序,您可以使用find
,sort
和head
或tail
来获取最旧/最新的目录。该ls
方法使用 ctime 属性进行排序。
答案3
尝试使用:
rm $(ls -t1 | tail -n 1)