【问题标题】:'find' files containing an integer in a specified range (in bash)“查找”包含指定范围内的整数的文件(在 bash 中)
【发布时间】:2018-08-17 10:17:54
【问题描述】:

您可能认为我已经在某个地方找到了答案,但我正在努力做到这一点。我想查找一些名称类似于

的日志文件
myfile_3.log

但是我只想找到数字在一定范围内的那些。我试过这样的事情:

find <path> -name myfile_{0..67}.log #error: find: paths must precede expression
find <path> -name myfile_[0-67].log #only return 0-7, not 67
find <path> -name myfile_[0,67].log #only returns 0,6,7
find <path> -name myfile_*([0,67]).log # returns only 0,6,7,60,66,67,70,76,77

还有其他想法吗?

【问题讨论】:

  • 不能用正则表达式表示范围。最好使用 find 来获取带有数字的文件,并使用另一个执行范围检查的工具(如 awk)过滤输出。

标签: linux bash find


【解决方案1】:

如果要使用正则表达式匹配整数范围,请在 find 命令中使用选项 -regex

例如要匹配从 0 到 67 的所有文件,请使用:

find <path> -regextype egrep -regex '.*file([0-5][0-9]|6[0-7])\.txt'

正则表达式有两部分:

  • [0-5][0-9] 匹配范围 0-59
  • 6[0-7] 匹配范围 60-67

注意选项-regextype egrep 具有扩展的正则表达式。
还要注意选项-regex 匹配整个文件名,包括路径,这就是.* 在正则表达式开头的原因。

【讨论】:

  • 你确定这是正确的吗?我收到错误“查找:路径必须在表达式之前”
  • 啊是的,但我仍然无法使用“或”语法。如果我选择一半或另一半很好,即 '.*default.log_[0-5][0-9]' (我的文件命名为 default.log_56 等),但它不匹配任何内容使用 ( | ) 语法。
  • 啊,看来都需要转义了,即\( \| \)
  • 而且该死的,这与 0-9 不匹配。所以需要“或”的另一部分:\([0-9]\|[0-5][0-9]\|6[0-7]\)
  • @oliv,我只想强调 --regextype long 选项似乎是 GNU find 独有的,并且在其他平台(macOS、FreeBSD 等)中不存在。
【解决方案2】:

使用 GNU Parallel,您可以简单明了地做到这一点,但不可否认的是效率不高:

parallel find . -name "*file{}.txt" ::: {0..67}

如果你想知道为什么我说它效率不高,那是因为它启动了 68 个 find 的并行实例——每个实例都在文件名中寻找不同的数字......但这可能没问题。

【讨论】:

  • 有一个类似的任务,本应该花费 PC 几分之一秒的事情需要几分钟并且风扇在吹,所有处理器内核都在 50%。至少似乎有效,但正如您所说,效率不高。
【解决方案3】:

下面将找到所有名为 myfile_X.log 的文件 - 其中 X 部分是一个介于 0-67 之间的数字。

find <path> -type f | grep -E "/myfile_([0-9]|[0-5][0-9]|6[0-7])\.log$"

说明:

  • -type f 查找类型为 file 的文件。

  • | 将文件路径通过管道传送到grep 以进行过滤。

  • grep -E "/myfile_([0-9]|[0-5][0-9]|6[0-7])\.log$" 执行扩展的 (-E) 正则表达式来查找路径的最后一部分(即文件名):

    • myfile_开头
    • 后跟一个 0-67 范围内的数字。
    • .log结尾

编辑:

或者,正如 cmets 中的 @ghoti 所建议的那样,您可以在 find 命令中使用 -regex 选项,而不是通过管道连接到 grep。例如:

find -E <path> -type f -regex ".*/myfile_([0-9]|[0-5][0-9]|6[0-7])\.log$"

注意: 正则表达式与前面显示的 grep 示例非常相似。但是,它以.*/ 开头,以匹配文件路径的所有部分,直到并包括最后的正斜杠。出于某种原因,我不知道,grep1 不需要 .*/ 部分。


脚注:

1如果有读者知道为什么 ERE 与 find 的 -regex 选项一起使用需要初始 .* 而与 grep 相同的 ERE 不需要 - 那么请发表评论.你会让我晚上睡得更好;)


【讨论】:

  • 假设范围可以表示为正则表达式,你为什么要parse 列表而不是使用find 的-regex 选项?
  • 感谢@ghoti 的评论 - 好问题!出于某种原因,当我尝试find -E &lt;path&gt; -type f -regex "\/myfile_([0-9]|[0-5][0-9]|6[0-7])\.log$" 时,它不起作用。
  • @ghoti - 我发现使用 find 的 -regex 选项(原因我不知道),表达式需要以 .*\/ 开头以匹配完整路径的初始部分在文件名之前。传送到grep 时没有必要。无论如何,我已经编辑了我的答案,以根据您的建议使用 -regex 选项提供一个示例。感谢您最初的评论/推动,促使我重新审视答案。
  • 在我的操作系统 (FreeBSD) 上,find 的正则表达式似乎是自动锚定的(即,如果您指定 foo 的正则表达式,它会将其解释为 ^foo$)。所以最初的.* 表示“零个或多个字符”,而正则表达式所针对的字符串是整个路径,而不仅仅是文件名。另外,我认为您不需要转义正斜杠,在这种情况下它没有特殊含义。所以.. 相当于find . -type f -name 'x*' 可能是find . -type f -regex '\.\(/[^/]*\)*/x[^/]*'。这东西很奇怪。
  • @ghoti - 我同意自动锚定,但是我的 ERE 以 $ 结尾,所以我假设在使用 find 的 -regex 时最初的 .*(“零个或多个字符”)是不必要的选项 - 显然情况似乎并非如此,这让我感到困惑。最初的.* 没有必要使用与grep 相同的ERE(它也在测试整个路径)。是的,同意转义的正斜杠是不必要的 - 已编辑答案以省略它。
【解决方案4】:

一种可能性是从多个可以由 glob 模式匹配的范围中建立范围。例如:

find . -name 'myfile_[0-9].log' -o -name 'myfile_[1-5][0-9].log' -o -name 'myfile_6[0-7].log'

【讨论】:

    【解决方案5】:

    您不能用正则表达式表示一般范围,尽管您可以为特定范围制作正则表达式。最好使用 find 来获取带有数字的文件,并使用另一个执行范围检查的工具(如 awk)过滤输出。

    START=0
    END=67
    while IFS= read -r -d '' file
    do
        N=$(echo "$file" | sed 's/file_\([0-9]\+\).log/\1/')
        if [ "$N" -ge "$START" -a "$N" -le "$END" ]
        then
            echo "$file"
        fi
    done < <(find <path> -name "myfile_*.log" -print0)
    

    在该脚本中,您对具有所需模式的所有文件执行find,然后遍历找到的文件,sed 用于捕获文件名中的数字。最后,将该数字与您的范围限制进行比较。如果比较成功,则打印文件。

    还有许多其他答案可以为您提供示例中特定范围的正则表达式,但它们并不通用。它们中的任何一个都可以轻松修改所涉及的范围。

    【讨论】:

    • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。
    • 上下文是作为问题的注释提供的,但你是对的,它应该在这里。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-07
    • 2010-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多