【问题标题】:How to list filenames with regular expression如何使用正则表达式列出文件名
【发布时间】:2016-05-05 21:08:47
【问题描述】:

我有多个不同后缀的文件:

file_12
file_34
file_1245
file_4567
file_4

我只想列出后缀为四位的文件。我知道我可以这样做

ls -l file_[0-9][0-9][0-9][0-9]

但是有没有更好的方法呢?

【问题讨论】:

标签: regex bash glob


【解决方案1】:

这取决于您所说的更好是什么意思,这归结为效率和便利性之间的权衡

  • 如果更好你的意思是更有效率

    • 否:您已经找到了最有效的方法:通配模式
      file_[0-9][0-9][0-9][0-9] 由外壳程序解析,在进程中,并将匹配的文件名传递给ls -l
    • Benjamin W.,在对该问题的评论中,有用地指出了一篇解释 shell 模式(通配模式)和 正则表达式之间差异的文章:http://mywiki.wooledge.org/BashGuide/Patterns
  • 如果更好你的意思是更方便

    • 使用正则表达式允许您使用量词(重复符号),这将表达式简化为 ^file_[0-9]{4}$^file_\d{4}$,具体取决于正则表达式方言。
    • 这是以要求以下任一为代价的:
      • 使用 Bash 的正则表达式匹配 =~ 运算符的带有条件的慢速 shell 循环 - 请参阅 rob mayoff's helpful answer
      • 涉及另一个理解正则表达式的外部实用程序,例如grep -E (egrep) - 请参阅drewyupdrew's helpful answer
        • 一个更强大的版本,仅使用awk 作为附加实用程序:
          ls -l | awk 'NF==9 && $NF ~ "^file_[0-9]{4}$"'

归根结底,抛开效率考虑,考虑到所需的额外代码,基于正则表达式的解决方案on balance都不能简化手头的任务。

因此,除非您的匹配逻辑过于复杂而无法使用 shell(通配符)模式进行建模(这里不是这种情况),否则我建议您坚持原来的方法。

【讨论】:

  • 完全正确 - It depends on what you mean by better.
【解决方案2】:

不,没有特别好的方法可以做到这一点。使用 shell glob 的方式很容易理解,而且几乎可以肯定足够高效。

这是一种更糟糕的方法:在[[ 命令中使用=~ 运算符一次匹配一个文件名,如下所示:

for f in *; do
    [[ $f =~ .*[0-9]{4} ]] && ls -l "$f"
done

这是另一种更糟糕的方法:使用find 命令:

find -E . -maxdepth 1 -regex '.*[0-9]{4}' -print0 | xargs -0 ls -l

【讨论】:

  • ++,但不需要管道到xargsfind -E . -maxdepth 1 -regex '.*[0-9]{4}' -exec ls -l {} + 就可以了,而且效率更高。
  • 因为我是老学生。
  • 我不确定老派在这种情况下的含义,但find-E-regex 不符合POSIX,xargs -0 也不符合。相比之下,find ... -exec ... + (不确定是在哪个版本的规范中引入的。但是);换句话说:在使用的功能中,find ... -exec ... + 应该可以安全使用,并且更简单,更高效。
【解决方案3】:

这是一个 glob 模式,但如果你想使用正则表达式,你可以使用 grep:

ls -l | egrep "file_[0-9]{4}$"

请记住,文件名的开头会被忽略,因此它仍会匹配名为 foo_file_1245 的文件。

 

编辑:@mklement0 建议并且我同意,如果您必须使用 egrep/regex,这是一种更好的方法:

ls -l | egrep '\sfile_[0-9]{4}$'

编辑 2:意识到上述语句会错误地匹配文件名,例如 someprefix file_1234。您可以更加安全并确保仅在最后一列(文件名)上使用 egrep:

ls -l | awk '{print $9}' | egrep "^file_[0-9]{4}$"

(但坦率地说,在这一点上,您的 glob 解决方案可能更快)

【讨论】:

  • 这匹配file_1234_blah
  • @BenjaminW。哦,你是对的,我们需要锚。我会编辑
  • @mklement0 啊,我想我明白你在做什么了。所以file 之前有任意数量的空格,然后是 4 位数字,对吗?这个怎么样:ls -l | egrep '\s+file_[0-9]{4}$'
  • @drewyupdrew:正确。它对您不起作用,因为您的 grep 实现的方言(与 Bash 版本无关):BSD grep 理解 \d 表示 digit,但 GNU grep没有(我的坏);使用ls -l | egrep '\sfile_[0-9]{4}$' 应该可以在两个平台上运行。
  • @drewyupdrew 很高兴,很高兴看到您以友好、协作的方式对 cme​​ts 的回答做出了回应。我可以建议您直接使用我们到达的命令更新您的答案吗?
【解决方案4】:

Perl 的救援:

perl -e 'system "ls", "-l", grep /^file_[0-9]{4}$/, glob "file_*";'

【讨论】:

  • 了解(并感谢您的修复),但是您正在启动 perl,它不是轻量级的,然后从中启动另一个 shell 实例,这让我觉得这是一种严厉的方法。 ls -l | egrep '\sfile_[0-9]{4}$' 编写起来更简单,可能更高效。
  • @mklement0:处理ls 的输出很危险。文件名可以包含空格、换行符等。
  • 我同意解析ls 输出不是最理想的,但通常仅针对嵌入在文件名中的换行符,这很少是真正的-世界关注,所以让我们把它放在一边。同意我的基于grep 的解决方案可能会产生误报。也就是说,OP 自己的方法既安全又高效,尽管由于无法简洁地重复表达式 [0-9] 而有些麻烦 - 但是,与您的解决方案相比,它仍然更简单、更有效。
  • 我想 egrep '^file_[0-9]{4}$' <(printf '%s\n' *) | xargs -I{} ls -l {}ls -l | awk 'NF==9 && $NF ~ "^file_[0-9]{4}$"' 会很好地工作(嵌入式换行符除外),但这些也强调了在这种情况下涉及额外的外部实用程序可能不值得麻烦。跨度>
【解决方案5】:

ls -l | grep file | grep -E '[0-9]{4}'

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-06
  • 2011-09-14
  • 2017-08-10
  • 2018-10-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多