【问题标题】:matching a specific substring with regular expressions using awk使用 awk 将特定子字符串与正则表达式匹配
【发布时间】:2011-05-25 21:39:41
【问题描述】:

我正在处理一个特定的文件名,并且需要从中提取信息。

文件名的结构类似于:“20100613_M4_28007834.005_F_RANDOMSTR.raw.gz”

使用 RANDOMSTR 是一个最多 22 个字符的字符串,其中可能包含(或不包含)格式为“-W[0-9].[0-9]{2}.[0-9]{3 }”。这个子串还具有以“-W”开头的独特特性。

我需要提取的信息是没有这个可选子串的 RANDOMSTR 的子串。

我想在 bash 脚本中实现这一点,到目前为止,我发现的最佳选择是将 gawk 与正则表达式一起使用。迄今为止我最好的尝试失败了:

gawk --re-interval '{match ($0,"([0-9]{8})_(M[0-9])_([0-9]{8}\\.[0-9]{3})_(.)_(.*)(-W.*)?.raw.gz",arr); print arr[5]}' <<< "20100613_M4_28007834.005_F_OTHER-STRING-W0.40+045.raw.gz"
OTHER-STRING-W0.40+045

预期结果是:

gawk --re-interval '{match ($0,$regexp,arr); print arr[5]}' <<< "20100613_M4_28007834.005_F_SOME-STRING.raw.gz"
SOME-STRING
gawk --re-interval '{match ($0,$regexp,arr); print arr[5]}' <<< "20100613_M4_28007834.005_F_OTHER-STRING-W0.40+045.raw.gz"
OTHER-STRING

怎样才能得到想要的效果。

谢谢。

【问题讨论】:

  • 您提到子字符串具有"-W[0-9].[0-9]{2}.[0-9]{3}" 模式,但您的示例输入包含...W0.40+045.raw.gz。您需要同时满足这两种需求吗?
  • 我没有将“.raw.gz”作为子字符串的一部分。
  • 抱歉,我的意思是要提请注意加号,它不会被您的模式覆盖。
  • 意思是模式只是为了匹配我不想要的来自 RANDOMSTR 的部分,而不是整个字符串。 (我无法编辑我之前的评论)
  • 好吧,我有点懒,只是放了一个“。”在加号的地方。它与字符串匹配,所以对我来说没问题。但最后我并没有真正使用那种模式,使用“(-W。*)”对我来说就足够了。子字符串的模式仅作为参考提供,以防万一。

标签: regex bash gawk


【解决方案1】:

虽然 grep 解决方案确实非常好,但 OP 并未提及操作系统,而且 -P 选项似乎仅在 Linux 中可用。在 awk 中执行此操作也非常简单。

$ awk -F_ '{sub(/(-W[0-9].[0-9]+.[0-9]+)?\.raw\.gz$/,"",$NF); print $NF}' <<EOT
> 20100613_M4_28007834.005_F_SOME-STRING.raw.gz
> 20100613_M4_28007834.005_F_OTHER-STRING-W0.40+045.raw.gz
> EOT
SOME-STRING
OTHER-STRING
$ 

请注意,这会在“20100613_M4_28007834.005_F_OTHER-STRING-W0_40+045.raw.gz”上中断。如果这是一个风险,并且-W 只出现在上面显示的位置,那么使用类似的东西可能会更好:

$ awk -F_ '{sub(/(-W[0-9.+]+)?\.raw\.gz$/,"",$NF); print $NF}'

【讨论】:

    【解决方案2】:

    无法让不情愿的量词运行,但依次运行两个正则表达式就可以了:

    sed -E -e 's/^.{27}(.*).raw.gz$/\1/' << FOO | sed -E -e 's/-W[0-9.]+\+[0-9.]+$//'
    20100613_M4_28007834.005_F_SOME-STRING.raw.gz
    20100613_M4_28007834.005_F_OTHER-STRING-W0.40+045.raw.gz
    FOO
    

    【讨论】:

    • sed -E 's/(-W[0-9].[0-9]{2}.[0-9]{3})?\.raw\.gz$//;s/.*_//' ...您不需要多个管道。 (对于所有 Linux 用户,请使用 sed -r 而不是 sed -E。)
    • 是的,完全正确。 sed -e 将执行一系列命令。我应该重写我的一个脚本:)
    【解决方案3】:

    这里的困难似乎在于可选的(-W.*)? 之前的(.*) 吞噬了后面的文本。使用非贪婪匹配也无济于事。不幸的是,我的 regex-fu 太弱了,无法解决这个问题。

    如果您不介意多通道解决方案,那么更简单的方法是首先通过删除尾随的.raw.gz 和可能的-W* 来清理输入。

    str="20100613_M4_28007834.005_F_OTHER-STRING-W0.40+045.raw.gz"
    echo ${str%.raw.gz}  | # remove trailing .raw.gz
         sed 's/-W.*$//' | # remove trainling -W.*, if any
         sed -nr 's/[0-9]{8}_M[0-9]_[0-9]{8}\.[0-9]{3}_._(.*)/\1/p'
    

    我使用了 sed,但你也可以使用 gawk/awk。

    【讨论】:

      【解决方案4】:

      您需要能够使用环视,我认为 awk/gawk 不支持,但 grep -P 支持。

      $ pat='(?<=[0-9]{8}_M[0-9]_[0-9]{8}\.[0-9]{3}_._)(.*?)(?=(-W.*)?\.raw\.gz)'
      $ echo "20100613_M4_28007834.005_F_SOME-STRING.raw.gz" | grep -Po "$pat"
      SOME-STRING
      $ echo "20100613_M4_28007834.005_F_OTHER-STRING-W0.40+045.raw.gz" | grep -Po "$pat"
      OTHER-STRING
      

      【讨论】:

      • 这绝对是更强的正则表达式! +1
      • 顺便说一句,除非我将其更改为 pat='(?&lt;=[0-9]{8}_M[0-9]_[0-9]{8}\.[0-9]{3}_._)(.+?)(?=(-W.*)?\.raw\.gz)',否则对我不起作用,即我必须使用 (.+?) 而不是 (.*?)
      • @Shawn: (.+?) 可能更好,但它对我有用,如图所示。我刚刚复制并粘贴了我的答案中的行以再次测试它并且它可以工作(无论哪种方式)。
      • 谢谢两位!它也适用于我,但只使用 Shawn 的变体。可惜我的 grep-fu 没有我的 awk-fu 强。如果经过一些试验后我不能得到我需要的结果(超出本期的范围),我会回复你。 :)
      • (.*?) 在我在 RHEL5 机器上运行它时有效,但在 RHEL4 上给出了空结果。奇怪的是,grep 的版本是相同的(2.5.1),但 Bash 的版本不同(3.2 与 3.0)。我希望它是 grep 的版本,而不是 Bash。
      猜你喜欢
      • 2013-04-13
      • 1970-01-01
      • 2022-01-17
      • 2023-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-06
      相关资源
      最近更新 更多