【问题标题】:How to delete all files in ~/Downloads that have not been touched, added, or opened in the last 30 days?如何删除 ~/Downloads 中最近 30 天内未触摸、添加或打开的所有文件?
【发布时间】:2022-06-15 23:47:11
【问题描述】:

我正在尝试创建一个自动化工作流或应用程序,当它被激活时,会删除我的下载文件夹中在过去 30 年内未以任何方式创建、修改、添加、打开或访问的所有文件和子文件夹天。

我试过这样过滤:

但这并不能真正完成我想要的工作。首先,没有按“添加日期”过滤的选项,我真的很喜欢。其次,我希望将子文件夹优先于该子文件夹的内容。例如,我今天添加了一个文件夹,但该文件夹中的文件有一个很久以前的“添加日期”。我的偏好是忽略该文件夹,包括其内容,因此删除。

然后我在另一个 Stack Overflow 线程(或者至少是某个 Stack Exchange 站点)中读到有人建议改用 bash 脚本。例如这样的事情:

$ find "$HOME/Downloads" -type fd -mtime +30d -atime +30d -iname '*.*'

但即使这样似乎也没有过滤掉我想要过滤掉的确切项目。

为了清楚起见,我想删除我的下载文件夹中在过去 30 天内未添加、打开、创建或修改的所有内容。如果有任何子树在过去 30 天内添加、打开、创建或修改了该子树中的 任何 个文件夹或文件,那么我想要整个子树被忽略并保持不变。有谁能帮帮我吗?

【问题讨论】:

  • 你可以做你正在做的事情,但是你的语法是错误的。问题是您无法很好地控制时间段。 -mtime 从今天开始测量时间,因此不考虑从那时到现在的小时数。使用你的命令,这可能是“足够接近”,你会做find ~/Downloads -mtime 29 -atime 29(你可以在确认列表是你想要的之后添加-delete)。对于 30 天内未访问的文件/目录,您需要 29 - 从今天开始的 24 小时期间。 (请参阅我的答案以更好地控制时间部分)

标签: bash macos automator


【解决方案1】:

如果您不想使用 find,这是另一种方法。适用于文件夹、文件。注释掉rm -rf 进行确认。

#!/bin/bash
compareDate=$(date -d "30 days ago" '+%Y%m%d')
for f in ~/Downloads/*;
do
    fileDate=$(date -r "$f" -u "+%Y%m%d")
    if [ ! "$fileDate" -gt "$compareDate" ];then
        echo Deleting - "$f";
        rm -rf  "$f"
    else 
        echo Keeping - "$f"
    fi
done

【讨论】:

    【解决方案2】:

    您可以使用find-newerXY 选项(您否定)来执行此操作,其中XY 等于mt 用于修改时间,at 用于访问时间,ct 用于创建时间。您只需传递 -delete 即可删除匹配的文件名。你可以这样做:

    d=$(date -d "30 days ago" '+%F %T')     # get date and time 30 days ago
    find ~/Downloads -type f ! -newermt "$d" ! -newerat "$d" ! -newerct "$d" -delete
    

    (选项的顺序很重要,因为它们被评估为一个表达式,如果你把-delete放在第一位,它将删除~/Download路径下的所有文件,因为在@987654331之前没有任何东西可以修改文件列表遇到@)

    注意:在不使用 -delete 的情况下进行测试,以确保它返回您期望的列表,然后重新添加选项以实际删除文件。


    关于将问题更改为是否有任何文件在 Mod、Access 或 Change 中较新 -- 将所有内容保存在该目录中的想法

    编辑后,子目录中的任何文件都将阻止删除子目录中的任何文件,这将阻止对 find 的单个调用有帮助,因为 find 在不知情的情况下一次处理单个文件对其他文件的测试是如何进行的。

    在这里,我的想法是更多地循环遍历~/Downloads 下的目录,依赖于设置的globstar。您将更改到您的 "$HOME" 目录(在脚本中),因此 **/ 搜索生成的路径将与 "$HOME" 相关,而不会在它们前面附加其他无关的 /home/user 路径组件。

    创建一个短函数,循环遍历正在处理的目录中的每个文件,如果任何一个文件在修改、访问或更改时更新,则对该目录不做任何操作,所有文件都将保存。

    使用stat 快速实现使用日期和模式,访问和更改时间(以秒为单位),您可以这样做:

    #!/bin/bash
    
    shopt -s globstar     # endable globstar globbing
    
    dt=$(date -d "30 days ago" '+%s')   # 30 days ago in seconds since epoch
    
    cd "$HOME" || exit 1  # change to home directory path globbing to Downloads
    dld="Downloads"       # set Downloads varabile 
    
    # function returns 0 if no files in dir with access or mod time in 30 days
    # returns 1 otherwise (don't remove)
    nonenewerthan30 () {
      local dir="$1"
      [ -d "$dir" ] || return 1   # validate it is a dir
      for f in "$dir"/*; do       # loop over files in dir
        [ -d "$f" ] && continue   # skip any directories in dir
        [ $(stat -c %X "$f") -gt "$dt" ] && return 1  # mod time since epoch
        [ $(stat -c %Y "$f") -gt "$dt" ] && return 1  # access time since epoch
        [ $(stat -c %Z "$f") -gt "$dt" ] && return 1  # change time since epoch
      done
      
      return 0    # directory can be removed.
    }
    
    
    for d in "$dld"/**/; do               # loop Downloads and all subdirs
      d="${d%/}"                          # remove trailing '/'
      [ "$d" = "$dld" ] && continue       # skip Downloads until subs processed
      printf "\nprocessing: %s\n" "$d"
      nonenewerthan30 "$d" && {           # call func, on 0 return, remove sub
        echo "  can remove $d"
        # rm -r "$d"           # uncomment after your verify behavior
      }
    done
    

    目前它会跳过处理Downloads 中的文件,直到所有子目录完成。您需要跟踪文件是否保留在任何级别,以了解是否将它们从Downloads 中删除甚至是一种选择。添加我留给你的逻辑。

    【讨论】:

    • 感谢您的回答。当我测试它时,我可以看到该列表仍然包含一个确实早于 30 天的文件,但是,该文件上方的文件夹具有今天的“添加日期”,所以我希望该文件不被理会.你知道有什么办法吗?我有一种预感,我需要使用两个不同的命令,一个命令选择太多文件,然后一个命令使用第一个命令的输出并过滤掉错误的结果。我目前正在尝试使用 mdfind 看看我是否可以让它工作。
    • 嗯,这很奇怪。由于-type ffind 限制为仅文件(您可以删除-type,它默认为所有文件/目录),因此基于上述命令的列表应仅包含 mtime 超过 30 天的文件、atime 和 ctime.. 相邻的表达式(例如,! newermt ... ! newerat ... 被 AND 运算在一起,所以选择的文件必须通过所有 3 个条件。所以如果“添加日期”(ctime)是今天,它不应该是包括在内。稍等,我会给你一个测试以在该文件上运行,以便我们确定。
    • ls -l file 显示什么? (mtime) 和ls -cl file 显示什么? (ctime)最后是ls -ul file(atime)?这至少会让我们看到文件系统认为所有 3 个时间戳是什么。您可以使用stat file 同时查看所有 3 个时间戳。
    • 好的,我收到了您的编辑,现在根据编辑更好地理解您的评论。 find 提供 Per-File 测试,不会根据其他文件时间戳聚合条件。您想要做的是可行的,但不是通过一次调用find。在这里,您必须获取每个目录和子目录并遍历文件以找到最后修改、创建和访问的文件,然后只有该文件在每个条件下也超过 30 天,您是否会删除其中的文件那个子树。您可以一次循环遍历一个目录。
    • 我还没有尝试过你的脚本,也许你的脚本正是我想要的。但我有一种感觉,也许只需要 2 行代码就可以完成。第一个是这样的:mdfind -onlyin "$HOME/Downloads" '(kMDItemDateAdded > $time.today(-1M) || kMDItemLastUsedDate > $time.today(-1M) && kMDItemContentCreationDate > $time.today(-1M) || kMDItemFSContentChangeDate > $time.today(-1M))' | grep -E 'Downloads/[^/]+$'。它列出了所有小于 30 天的根文件,以及任何子文件或子文件夹小于 30 天的所有子文件夹。
    最近更新 更多