在bash中查找包含指定范围内整数的文件

5

您可能认为我可以在某个地方找到答案,但我却很难做到。我想找一些类似于以下名称的日志文件:

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)过滤输出。 - Poshi
5个回答

6

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

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

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

正则表达式有2个部分:

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

注意选项-regextype egrep以使用扩展的正则表达式。
同时注意选项-regex匹配整个文件名,包括路径,这就是为什么在正则表达式开头加上.*的原因。


啊,好的,但我仍然无法让“或”语法起作用。如果我选择其中一半,比如“.*default.log_[0-5][0-9]”(我的文件名类似于default.log_56等),那么它就可以正常匹配,但是使用(|)语法时它不匹配任何内容。 - Ben Farmer
啊,看来它们都需要转义,即 \( \| \) - Ben Farmer
1
而且,该正则表达式与0-9不匹配,真是让人沮丧。所以需要添加另一个部分来表示“或”的条件:\([0-9]\|[0-5][0-9]\|6[0-7]\) - Ben Farmer
一些 find 实现包括一个 -E 标志,它指示正则表达式解析器假定 ERE 而不是 BRE。 - ghoti
1
@oliv,我只想强调一下,--regextype长选项似乎是GNU find独有的,在其他平台(如macOS、FreeBSD等)中不存在。 - ghoti
显示剩余7条评论

4
您可以使用GNU Parallel简单而简明地完成此操作,但承认效率不是很高:
parallel find . -name "*file{}.txt" ::: {0..67}

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


1
之前遇到了类似的任务,本应只需几分之一秒就能完成的工作却需要几分钟,并且电脑的风扇在猛烈旋转,所有的处理器核心都达到了50%。虽然看起来至少是在工作,但像你所说的,效率并不高。 - cardamom

1
以下代码将查找所有名为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结尾

编辑:

另外,如评论中@ghoti所建议的那样,您可以使用-regex选项而不是管道到grep来利用find命令。例如:

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

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


脚注:

1如果有读者知道为什么使用find的 -regex 选项时需要初始的 .*,而相同的ERE在grep中不需要 - 请留言。这会让我晚上睡得更好 ;)



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

0
一个可能的方案是从几个可以被glob模式匹配的范围中构建出该范围。例如:
find . -name 'myfile_[0-9].log' -o -name 'myfile_[1-5][0-9].log' -o -name 'myfile_6[0-7].log'

-1

您无法使用正则表达式表示一般范围,尽管您可以为特定范围制作正则表达式。最好使用 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来捕获文件名中的数字。最后,将该数字与您的范围限制进行比较。如果比较成功,则打印该文件。
还有许多其他答案为您提供示例中特定范围的正则表达式,但它们不是通用的。其中任何一个都允许轻松修改所涉及的范围。

尽管此代码可以回答问题,但提供有关如何以及/或为什么解决问题的额外上下文会改善答案的长期价值。 - Nic3500
上下文作为评论提供给了问题,但你是对的,它应该在这里。 - Poshi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接