我知道有“查找”,我使用类似的方法find "/backup.stats/30days" -mtime +30 -type f
,但我想知道是否有人知道如何按月找出最旧的文件;并非所有月份都是 30 天,我只希望能够按月查找和复制最旧的文件。
例如,如果某个文件是在 3 月 31 日晚上 11:59 创建的,则将其复制到某个位置。但如果出于某种原因,该月最早的文件是 3 月 30 日凌晨 2:30,则将其复制到某个位置。
我的想法是,我有一个文件夹,里面有家庭网络过去 30 天的统计信息,我想确保每个月在不同的文件夹中至少保存一份副本。
有人有什么想法吗?
编辑:正如下面的评论所指出的那样。我实际上指的是包含整个月统计数据的文件。因此,当月份滚动时,它将是最新生成的文件。
答案1
解决方案
使用 GNU 工具集:
find . -type f -exec sh -c 'LC_ALL=C stat --printf="%.Y|%y|%n\0" -- "$@"' find-sh {} + \
| LC_ALL=C sort -zr -t '|' -k 1,1 \
| LC_ALL=C sort -zsu -t '|' -k 2.1,2.7 \
| cut -d '|' -zf 3- \
| tr '\0' '\n'
根据您的需要调整find
(最多但不包括)的调用。-exec
解释
对于每个到达 的文件
-exec
,LC_ALL=C stat --printf='%.Y|%y|%n\0'
都会运行。其输出包括如下行1711542530.762649374|2024-03-27 13:28:50.762649374 +0100|./path to/something
其中第一个
|
分隔字段是上次数据修改的时间,以 Epoch 以来的秒数表示(精准);第二个字段是最后修改数据的时间,便于阅读。每行都以空字符结尾,因此路径名中的换行符(如果有)应该是安全的。只有前两个|
字符在后面才重要,它们肯定都来自格式,因此|
路径名中的换行符(如果有)也应该是安全的(请参阅下面的解释cut
)。我过去常常
LC_ALL=C
使格式独立于您当前的语言环境。注意LC_ALL=C find …
会影响find
它运行的所有内容,一般来说这可能是不想要的;所以-exec stat …
我没有使用-exec sh -c …
和这种方式,而是能够LC_ALL=C
只为设置stat
。然后第一个
sort
根据第一个|
-separated 字段对行进行排序。与最近修改的文件相关的行将排在最前面。我们的格式非常严格,因此C
可以使用语言环境中的默认排序方式。第二个
sort
仅考虑第二个字段(在示例中)YYYY-MM
的(年月)部分,并且由于(),它每个仅传递一行。使用(2024-03
-u
--unique
YYYY-MM
-s
--stable
)这是与每个最近修改的文件相关的行YYYY-MM
,因为第一个sort
已经将最近的文件放在第一位。然后,每行
cut
打印|
从第 3 个到最后一个以 - 分隔的字段。每行中都有一个路径名。正式的路径名包含(一个或多个)字符,|
将构成第 3 个、第 4 个以及可能更后面的字段,但由于输出中的字段也将以 分隔|
,因此输出无论如何都是准确的路径名。最后
tr
将空字节转换为换行符,只是为了使输出易于阅读(但也可能产生歧义)。
笔记
如果要进一步处理结果,请尽可能将其保留为以空字符结尾的字符串形式。换句话说:期望以空字符结尾的字符串(例如
xargs -r0 …
)并放置在 而tr
不是 的工具比期望以换行符结尾的字符串并放置在 之后的工具更好tr
。Linux 时间戳是只有数字,没有时区概念. 你的
stat
意志将它们“翻译”成你的当前的YYYY-MM
时区。特别是,它会根据您当前的时区分配文件,这在不同的时区会产生不同的结果。例如,如果您在印度(文件是在印度已经经历了新月的几个小时时修改的),则将2024-04-01 00:00:00 UTC
分配给,但如果您身在墨西哥(文件是在墨西哥还剩下旧月的几个小时时修改的),则将分配给 。2024-04
2024-03
您可能想知道我们是否真的需要两个
sort
。乍一看我们不需要%.Y
fromstat
,按定义良好的排序%y
应该足够了。嗯,我不需要。考虑以下两行:1698542400.000000000|2023-10-29 02:20:00.000000000 +0100|./newer 1698540000.000000000|2023-10-29 02:40:00.000000000 +0200|./older
此示例在
Europe/Warsaw
时区中。newer
文件确实比文件新older
,自纪元以来的秒数显示了这一点,并且顺序就像从第一个开始sort
:最新优先。但是,如果我按秒|
分隔的字段排序并尝试实现“最新优先”,那么它就会反过来出现。事实是,02:40
对于文件,由于那年夏令时结束,我的时钟从调回older
之前发生;对于文件,发生在 之后。没有歧义,字符串和携带信息;但不理解格式。这就是为什么在解决方案中我们首先按自纪元以来的秒数排序,然后我们使用秒来选择最新的(我的意思是真的最新的)文件。03:00
02:00
02:20
newer
+0200
+0100
sort
sort
YYYY-MM
我认为
date --reference
可以使用 GNU 来stat
获取文件的修改时间。stat
但我选择了它。如果您对某个 的结果感兴趣
YYYY-MM
,请将 放在和 第一个grep
之间。例如,可能是:find
sort
2024-02
LC_ALL=C grep -Zza '^[^|]*|2024-02'
空结果表示该月没有修改任何文件。这样
grep
整个解决方案最多应将一行以空字符结尾的字符串传递给tr
。如果将多行以空字符结尾的字符串传递给 ,tr
则表示我的解决方案存在错误。您的语言环境可能使用 UTF-8,但一般来说,路径名可能包含在 UTF-8 中无效的字节序列。我使用了
LC_ALL=C grep -a
,所以grep
不应该抱怨。find-sh
解释如下:中的第二个 sh 是什么sh -c 'some shell code' sh
?