【问题标题】:Recursively looking for a list of file types递归查找文件类型列表
【发布时间】:2019-01-12 15:27:33
【问题描述】:

我想使用 bash 删除目录中不在文件扩展名关联数组中的所有文件。 (例如,删除目录中不是图像文件的所有文件)

This question 非常清楚地回答了如何为单个文件扩展名执行此操作,但我不确定如何为整个列表执行此操作。

目前我在做this

for f in $(find . -type f ! -name '*.png' -and ! -name '*.jpg' ); do rm "$f"; done

但在每个文件类型的括号内添加大量“-and -name '*.aaa'”列表似乎很难看。

有没有办法将find 传递给关联数组,例如

declare -A allowedTypes=([*.png]=1 [*.jpg]=1 [*.gif]=1)

还是我只需要添加很多“-and !-name ___”?

谢谢!

【问题讨论】:

    标签: bash search filter associative-array


    【解决方案1】:

    不需要首先使用find int 的整个想法。 bash 中的 shell globbing 支持足以满足此要求。 bash shell 提供了一个扩展的 glob 支持选项,您可以使用该选项在不以您要忽略的扩展名结尾的递归路径下获取文件名。

    扩展选项为extglob,需要使用shopt 选项进行设置,如下所示。此外,您可以更多地使用几个选项,即nullglob,其中一个不匹配的 glob 被完全扫除,替换为一组零词。而globstar 允许递归遍历所有目录

    shopt -s extglob nullglob globstar
    

    现在您需要做的就是形成 glob 表达式以排除 *.png*.jpg*.gif 类型的文件,您可以执行以下操作。我们使用一个数组来填充 glob 结果,因为当正确引用和扩展时,带有特殊字符的文件名将保持不变

    fileList=(**/!(*.jpg|*.gif|*.png))
    

    选项** 是递归遍历子文件夹,!() 是一个否定操作,不包括其中列出的任何文件扩展名。现在要打印实际文件,只需执行

    printf '%s\n' "${fileList[@]}"
    

    如果您的意图是删除所有已识别的文件,则无需将 glob 结果存储在数组中。在编写需要使用 glob 结果的简单 shell 脚本时,可以使用数组方法。但是对于删除文件的情况,您可以使用rm 命令。

    首先,您可以检查返回的文件是否符合预期,一旦您确认可以在表达式上使用rm。使用ls 查看文件是否按预期列出

    ls -1 -- **/!(*.jpg|*.gif|*.png)
    

    现在确认要删除的文件后,请自行承担rm 的风险。

    rm -- **/!(*.jpg|*.gif|*.png)
    

    【讨论】:

    • 我最初说“打印”是为了简化,但实际上我需要删除所有不在扩展名列表中的文件,所以你最终的“printf”实际上并不适合我需要(也不只是将所有文件名保存到列表中。我已经修改了我的问题,你可以修改你的答案来删除吗?
    • globstar 至少需要bash v4,我相信,因此 macOS 用户需要安装它,因为 Apple 仅提供 bash v3.2 左右。
    • 如果文件或扩展名是大写的怎么办?您必须使用所有[jJ][pP][gG] 废话吗?或者你可以使用shopt -s nocaseglob吗?
    【解决方案2】:

    假设allowedTypes 仅包含受信任的输入和有效的后缀。

    第一个 sn-p 支持多级后缀,如tar.gz。它使用find、一个正则表达式和一个允许的后缀列表allowedTypes

    allowedTypes=(png gif jpg)
    
    # keepTypes='png|gif|jpg'
    keepTypes="$(echo "${allowedTypes[@]}" | tr ' ' '|')"
    
    find . -type f -regextype awk ! -iregex '(.*).('"$keepTypes"')' -exec echo rm {} \;
    

    如果你想保留你的关联数组,那么你可以使用下面的 sn-p. 它需要额外的工作来支持多级文件后缀。

    declare -A allowedTypes=([*.png]=1 [*.jpg]=1 [*.gif]=1)
    
    keepTypes="$(echo "${!allowedTypes[@]}" | tr ' ' '|' | tr -d '.*')"
    

    如果有办法用内置工具而不是tr 替换分隔符,那就太好了,但我没有找到。 ${allowedTypes[@]//\ /test} 没有替换项目之间的空格。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-04-09
      • 2020-07-17
      • 2013-06-24
      • 2023-03-14
      • 1970-01-01
      • 2012-09-21
      • 2014-06-21
      相关资源
      最近更新 更多