【问题标题】:output occurrences of multiline pattern within text file in bash在bash中的文本文件中输出多行模式的出现
【发布时间】:2020-05-21 06:12:24
【问题描述】:

我想编写一个 bash 脚本,它可以识别文本中与多行模式匹配的标签,以便我可以使用识别标签进一步处理嵌套标签以供以后处理。我已经搜索了多个问题,但它们似乎都以某种方式不足,难以取得进展。我成功的是能够匹配模式并获得匹配的行,但是它作为单个输出出现(我相信)。首先是我正在测试的示例文本文件。

random words to put here: dresser car street space 
*
********************************************************************************
********************************************************************************
-->
         interested data: name="someFile_1.txt"random data
      endMultilinePattern
   <!--****************Random comment***************-->
      startMultilinePattern id="someFileTag_2"
         interested data: name="someFile_2.txt"random data
      endMultilinePattern
   <!--****************Random comment***************-->
      startMultilinePattern id="someFileTag_3"
        interested data: name="someFile_3.txt"random data
      endMultilinePattern      
   some random data body
      some random nested data filepath="/" uuid="randomcharacters"random data
   some random data body
 more random data
 endMultilinePattern
      startMultilinePattern id="someFileTag_2"
         interested data: name="error_someFileTag_2.txt"random data
      endMultilinePattern
   <!--****************Random comment***************-->

以下是我得到的一些输出以及导致它们的答案。可能是我自己理解不够,不知道如何正确使用命令。首先,我感兴趣的 id 在startMultilinePattern id="someFileTag_2"&gt; 中,稍后我将在文件中使用id 来匹配使用该id 的其他标签。其次,我想在interested data: name="..."random data 标记中获取属性name,以便在文件系统中搜索该文件以进行进一步处理。在这个问题中,我现在要做的就是获取startMultilinePattern&gt; ... multi-line match ... endMultilinePattern,然后在interested data: name="..."random data 标签中获取文件名。我们开始吧:

以下使用了 perl 的 grep 中的 -P 选项,虽然它得到了正确的输出,但我似乎无法读入数组并输出每个多行匹配。
源:grep (bash) multi-line pattern

$ $ grep -Pzon "((startMultilinePattern )(.|\n)*?(endMultilinePattern))" test.txt | while read -a grepOut; do POS=$((POS+1)) && echo "0=${grepOut[0]}, 1=${grepOut[1]}, 2=${grepOut[2]}, 3=${grepOut[3]}}";done                                                               0=1:startMultilinePattern, 1=id="someFileTag_2", 2=, 3=}
0=interested, 1=data:, 2=name="someFile_2.txt"random, 3=data}
0=endMultilinePattern1:startMultilinePattern, 1=id="someFileTag_3", 2=, 3=}
0=interested, 1=data:, 2=name="someFile_3.txt"random, 3=data}
0=endMultilinePattern1:startMultilinePattern, 1=id="someFileTag_2", 2=, 3=}
0=interested, 1=data:, 2=name="error_someFileTag_2.txt"random, 3=data}

# grep command by itself provides the following output: 
1:startMultilinePattern id="someFileTag_2"
         interested data: name="someFile_2.txt"random data
      endMultilinePattern1:startMultilinePattern id="someFileTag_3"
        interested data: name="someFile_3.txt"random data
      endMultilinePattern1:startMultilinePattern id="someFileTag_2"
         interested data: name="error_someFileTag_2.txt"random data
      endMultilinePattern

使用 sed 大概应该更合适,我找到了这个有趣的答案,但我无法让它工作。它使用了一些我不明白的时髦的开始关键字。 源:https://unix.stackexchange.com/questions/112132/how-can-i-grep-patterns-across-multiple-lines

sed -n '/\startMultilinePattern /{:start /endMultilinePattern/!{N;b start};/startMultilinePattern .*\n.*\n.*endMultilinePattern/p}' test.txt

此外,下面的 sed 命令据说可以作为它的许多答案,但可能是它的旧功能。我无法让它工作,因为输出看起来不像预期的那样。它包括我不想要的部分文本,即&lt;some random data body ....。 源:https://unix.stackexchange.com/a/112134/388443

$ sed -e '/startMultilinePattern /,/endMultilinePattern/!d' test.txt
      startMultilinePattern id="someFileTag_2"
         interested data: name="someFile_2.txt"random data
      endMultilinePattern
      startMultilinePattern id="someFileTag_3"
        interested data: name="someFile_3.txt"random data
      endMultilinePattern
      startMultilinePattern id="someFileTag_2"
         interested data: name="error_someFileTag_2.txt"random data
      endMultilinePattern

还有其他答案有他们自己的做法。有些人使用 awk,我不知道 awk 所以没有尝试,我也不能使用 pcregrep,因为我没有安装它的 root 权限。据我了解, grep -P 或多或少等同于 pcregrep。想法?

【问题讨论】:

  • Don't Parse XML/HTML With Regex. 我建议使用 XML/HTML 解析器 (xmlstarlet, xmllint ...)。
  • 谢谢,我会检查一下,但 XML 不仅仅是问题的特定上下文的要求。文件类型和 XML 标签是间接的,而不是我唯一的输入文件。我会将文件视为一般文本,因此无论文件类型如何,我都在寻找正则表达式的一般解决方案。在我处理它时,我可能希望将此解决方案重用于其他类型的情况。
  • 重申我正在寻找的是:一种可以匹配多行模式的方法,我可以在给定文件中提取模式的每次出现以提取更多数据。看这篇文章,它很有趣,但后来它谈到了复杂性和 XML 复杂性 > 正则表达式复杂性,这就是为什么你不对 XML 使用正则表达式的原因。我理解这一点,但我相信我的输入文件过于简化和控制对于正则表达式来说过于复杂。见:stackoverflow.com/a/1758162/10421103

标签: regex bash sed grep


【解决方案1】:

请您尝试以下方法:

str="$(<"test.txt")"            # slurps all the file in a variable str
pattern='startMultilinePattern id="([^"]+)"[[:space:]]+interested data: name="([^"]+)"(.*)'
while [[ $str =~ $pattern ]]; do
    echo "${BASH_REMATCH[1]}"   # prints the id
    echo "${BASH_REMATCH[2]}"   # prints the filename
    str="${BASH_REMATCH[3]}"    # updates the variable str with the remaining substring
done

使用提供的示例输出:

someFileTag_2
someFile_2.txt
someFileTag_3
someFile_3.txt
someFileTag_2
error_someFileTag_2.txt

您可以将 id 和文件名存储在数组或关联数组中以供进一步使用。

[解释]

  • 它首先将整个文件读入一个变量str,包括换行符 字符以启用多行模式匹配。
  • 变量pattern是匹配子串的正则表达式 以 startMultilinePattern 后跟 id,空格包括 换行符,interested dataname 分配 shell 变量 ${BASH_REMATCH[@]}idname 和 匹配后剩余的子字符串。
  • 表达式$str =~ $pattern 测试字符串$str 以匹配 正则表达式$pattern。 在while 循环的帮助下,它会扫描整个文件直到结束。
  • 如果提供的示例是简化的并且您的实际文件有 标签的变化,您可能需要相应地调整pattern

【讨论】:

  • 那我试试看,$str =~ $pattern 有什么作用?
  • 是的,正如你所说的那样,我将不得不做更多的测试,看看它是否以编程可用的方式提供输出,以满足我的预期目的。谢谢你的回答!
  • @LeanMan 感谢您的回复。我在回答中添加了解释。希望它会有所帮助。
猜你喜欢
  • 1970-01-01
  • 2021-08-22
  • 2013-11-25
  • 2014-06-17
  • 2020-08-04
  • 2017-03-15
  • 2014-09-09
  • 2023-03-09
  • 1970-01-01
相关资源
最近更新 更多