提取分隔符之间的所有事件,因为它们每行可以出现多个

Extract all ocurrences between delimiters being that they can appear more than one per line

我有一个文档 file.yaml,其中包含要替换的占位符之类的内容:

class: ##TOPIC##-area
  name: myClass
type: Class
secretKey: private-##SECRET_KEY##

到目前为止,我已经使用 grep 获取占位符的值

grep -P '(?<=##).*(?=##)' file.yaml

然后,我有那些值:

TOPIC
SECRET_KEY

现在,我们必须引入每行可以有多个占位符的新属性

class: ##TOPIC##-area
  name: myClass
type: Class
secretKey: private-##SECRET_KEY##-encoded-##SUFFIX_CODE##

因此,grep 不再起作用,因为输出变为:

TOPIC
SECRET_KEY##-encoded-##SUFFIX_CODE

但是,我想要

TOPIC
SECRET_KEY
SUFFIX_CODE

我接受各种建议和想法来解决这个问题。谢谢

编辑:想法是只获取那些占位符,而不是替换它们。抱歉造成误会。

使用否定先行从匹配中排除包含 ## 的子字符串。

(?<=##)((?!##).)*(?=##)

DEMO

请注意,这也会 return -encoded-,因为它也在一对 ## 之间。通常您不会得到重叠匹配,但环顾四周不被视为匹配的一部分,因此它们不算作重叠。

当您想使用 grep 时,请尝试使用

grep -Eo "##[^#]*##" file.yaml | tr -d '#'

使用 awk 你可以有一个多字符分隔符,看起来更简单:

awk -F'##' '{for (i=2; i<=NF;i+=2) {print $i}}' file.yaml

如果 sed 是一个选项,您可以尝试这个解决方案:

sed '/##.*##/!d
     s/##/\
/
     s/.*\n//
     s/##/\
/
     P;D' file

或者,一个简单的 bash 解决方案:

#!/bin/bash

while read -r line; do
    while [[ $line = *'##'*'##'* ]]; do
        line=${line#*'##'}
        printf '%s\n' "${line%%'##'*}"
        line=${line#*'##'}
    done
done < file

你得到那个输出的原因是因为 .* 是贪婪的。您可以改为使其非贪婪 .*?

如果只想输出匹配,可以在grep中添加-o

grep -oP '(?<=##).*?(?=##)' file.yaml

输出

TOPIC
SECRET_KEY
-encoded-
SUFFIX_CODE

如果只有大写占位符且中间可以有下划线,则可以使模式更具体一些。

grep  -oP "##\K[A-Z]+(?:_[A-Z]+)*(?=##)" file.yaml
  • ##字面匹配
  • \K忘记目前匹配的内容(清空当前匹配缓冲区)
  • [A-Z]+(?:_[A-Z]+)* 匹配 1+ 个大写字符,可选地由 _ 和大写字符
  • 重复
  • (?=##) 正面前瞻,向右断言 ##

看到一个regex demo.

输出

TOPIC
SECRET_KEY
SUFFIX_CODE

第一个解决方案: 使用 GNU awk,请尝试以下 awk 代码。简单的解释是,将 RS(记录分隔符)设置为 ##,直到下一次出现 #,然后出现 2 次 ##。然后根据显示的输出示例,仅通过删除结果中不需要的 ## 来打印匹配的行。

awk -v RS='##[^#]*##' 'RT{print substr(RT,3,length(RT)-4)}' Input_file


第二个解决方案: 对于任何 awk,请尝试以下程序。简单的解释是,使用 awkmatch 函数来匹配正则表达式 /##[^#]*##/(已经在上面的第一个解决方案中解释过);在 while 循环中打印每行中找到的所有匹配项。

awk '{while(match([=11=],/##[^#]*##/)){print substr([=11=],RSTART+2,RLENGTH-4);[=11=]=substr([=11=],RSTART+RLENGTH)}}' Input_file

如果Perl是您的选择,请您尝试:

perl -lne 'print  while /##(.+?)##/g' file.yaml
  • -l 选项将换行符附加到 print.
  • 的输出
  • -ne 选项与 sed.
  • 的选项大部分相似
  • </code>指的是正则表达式的捕获组1。</li> <li><code>while /pattern/g 语法允许在同一行中进行多个匹配。

使用 GNU awk 进行多字符 RS,RT:

awk -v RS='##[[:upper:]_]+##' 'RT{gsub(/##/,"",RT);print RT}' file
TOPIC
SECRET_KEY
SUFFIX_CODE