如何按一个月的最后一天查找文件?(或如何复制每月的最新文件)

如何按一个月的最后一天查找文件?(或如何复制每月的最新文件)

我知道有“查找”,我使用类似的方法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


解释

  1. 对于每个到达 的文件-execLC_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

  2. 然后第一个sort根据第一个|-separated 字段对行进行排序。与最近修改的文件相关的行将排在最前面。我们的格式非常严格,因此C可以使用语言环境中的默认排序方式。

  3. 第二个sort仅考虑第二个字段(在示例中)YYYY-MM的(年月)部分,并且由于(),它每个仅传递一行。使用(2024-03-u--uniqueYYYY-MM-s--stable)这是与每个最近修改的文件相关的行YYYY-MM,因为第一个sort已经将最近的文件放在第一位。

  4. 然后,每行cut打印|从第 3 个到最后一个以 - 分隔的字段。每行中都有一个路径名。正式的路径名包含(一个或多个)字符,|将构成第 3 个、第 4 个以及可能更后面的字段,但由于输出中的字段也将以 分隔|,因此输出无论如何都是准确的路径名。

  5. 最后tr将空字节转换为换行符,只是为了使输出易于阅读(但也可能产生歧义)。


笔记

  • 如果要进一步处理结果,请尽可能将其保留为以空字符结尾的字符串形式。换句话说:期望以空字符结尾的字符串(例如xargs -r0 …)并放置在 而tr不是 的工具比期望以换行符结尾的字符串并放置在 之后的工具更好tr

  • Linux 时间戳是只有数字,没有时区概念. 你的stat意志将它们“翻译”成你的当前的YYYY-MM时区。特别是,它会根据您当前的时区分配文件,这在不同的时区会产生不同的结果。例如,如果您在印度(文件是在印度已经经历了新月的几个小时时修改的),则将2024-04-01 00:00:00 UTC分配给,但如果您身在墨西哥(文件是在墨西哥还剩下旧月的几​​个小时时修改的),则将分配给 。2024-042024-03

  • 您可能想知道我们是否真的需要两个sort。乍一看我们不需要%.Yfrom stat,按定义良好的排序%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:0002:0002:20newer+0200+0100sortsortYYYY-MM

  • 我认为date --reference可以使用 GNU 来stat获取文件的修改时间。stat但我选择了它。

  • 如果您对某个 的结果感兴趣YYYY-MM,请将 放在和 第一个grep之间。例如,可能是:findsort2024-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

相关内容