Linux - 查找文件行中特定模式的所有出现

Linux - 查找文件行中特定模式的所有出现

我在 Linux 中有一个文件,其中包含如下行:

(memberOf=CN=Group1)(memberOf=CN=Group2)(memberOf=CN=Group3)(memberOf=CN=Group4)

我的目标是填充该行中存在的组列表,例如 -

Group1

Group2

Group3

Group4

答案1

pcregrepPerlC兼容的R正常E表达grep):

pcregrep -io1 '\(memberOf=CN=(.*?)\)'

或者直接使用perl

perl -lne 'print $1 while /\(memberOf=CN=(.*?)\)/gi'

(此处使用-ii标志作为 LDAP 属性名称不区分大小写)。

如果您的grep实现支持该-P选项(就像 GNUgrep在使用 PCRE 支持构建时所做的那样),那么它很可能也支持非标准选项-o,您可以这样做:

grep -iPo '\(memberOf=CN=\K.*?(?=\))'

这里使用\Kand 的前瞻运算符表示(memberOf=CN=and)不包含在输出中,因为grep其他实现pcregrep不支持输出捕获组。

使用 POSIX 实用程序,您可以执行大致等效的操作:

sed -n '
  /([mM][eE][mM][bB][eE][rR][Oo][fF]=[Cc][Nn]=\([^)]*\))\(.*\)/ {
    s//\
\1\
\2/
    s/.*\n\(.*\n\)/\1/
    P;D
  }'

如果该字符串存储在 shell 标量变量中,并且您希望将结果组存储在 shell 数组变量中,则使用 shell zsh,您可以执行以下操作:

set -o extendedglob

string='(memberOf=CN=Group1)(memberOf=CN=Group2)(memberOf=CN=Group3)(memberOf=CN=Group4)'
groups=()

: ${string//(#ib)[(]memberOf=CN=([^)]#)[)]/${groups[$#groups+1]=$match[1]}}

或者从 GNU 获取它grep

groups=(
  ${(0)"$(
    print -rN -- $string |
      grep -ziPo '\(memberOf=CN=\K.*?(?=\))'
  )"}
)

与 bash 4.4+ 相同:

readarray -td '' groups < <(
  printf '%s\0' "$string" |
    grep -ziPo '\(memberOf=CN=\K.*?(?=\))'
) && wait "$!"

答案2

使用 Raku(以前称为 Perl_6)

raku -ne 'put $/.join("\n") if m:g/\(memberOf\=CN\= <(.*?)> \)/;'

或者

raku -ne 'put $/.join("\n") if m:g/<?after \(memberOf\=CN\= > (.*?) <?before \) > /;'

简而言之,示例 1 使用<(…)>Raku 中的匹配分隔符,指示正则表达式引擎删除<(.*?)>.示例 2 使用<?after "pattern1" >正向前瞻和<?before "pattern2" >正向前瞻来隔离中间的所有内容。如果不区分大小写很重要,则只需将m:g('match-global') 更改为m:g:i('match-global case-insensitive') 即可。

输入示例:

(memberOf=CN=Group1)(memberOf=CN=Group2)(memberOf=CN=Group3)(memberOf=CN=Group4)

输出示例 (1):

Group1
Group2
Group3
Group4

或示例输出 (2):

Group1 Group2 Group3 Group4

想象一下,OP 有一个文件,其中包含多个与示例行类似的行,那么将所有输出全部返回到一行上可能是有意义的。对于上述任一示例,只需更改$/.join("\n")$/即可使每个逐行捕获按行返回(上面的输出 2)。

最后,OP 可以在“组”列表中编码位置信息(例如,左侧较低的组#,右侧较高的组#)。在这种情况下,将comb()匹配项 -out 并将其显示为可能更有意义.pairs,如下所示:

raku -ne '.raku.say for .comb(/<?after \(memberOf\=CN\= > (.*?) <?before \) > /).pairs;' 

0 => "Group1"
1 => "Group2"
2 => "Group3"
3 => "Group4"

https://docs.raku.org/language/regexes
https://raku.org

答案3

这个解决方案有点类似于 的@bxm,但用了一个sed步骤而不是纯粹的grep

grep -Eo '\(memberOf=CN=[^()]*\)' |
sed 's/(memberOf=CN=\(.*\))/\1/' 

输入:

(memberOf=CN=Group1)(memberOf=CN=Group2)(memberOf=CN=Group3)(memberOf=CN=Group4)
(memberOf=CN=GroupA1)
(memberOf=CN=GroupA2)(memberOf=CN=GroupA3)
(memberOf=CN=GroupA4)
(rememberOf=CN=GroupX1)(memberOf=CN=GroupX2)
(numberOf=CN=GroupX4)
(memberOf=CN=GroupB1)(memberOf=CN=GroupB2)(memberOf=CN=GroupB3)(memberOf=CN=GroupB4)

输出:

Group1
Group2
Group3
Group4
GroupA1
GroupA2
GroupA3
GroupA4
GroupX2
GroupB1
GroupB2
GroupB3
GroupB4

的输出grep显示在sed的输入上:

(memberOf=CN=Group1)
(memberOf=CN=Group2)
(memberOf=CN=Group3)
(memberOf=CN=Group4)
...

sed然后获取该输出并去掉前导(memberOf=CN=和尾随).

通过对行中的更多内容进行通配符,可以使此代码稍微更通用一些sed

grep -Eo '\(memberOf=CN=[^()]*\)' |
sed 's/.*=\(.*\))/\1/'

答案4

有很多方法可以解决这个问题,这里有一个解决方案,使用广泛可用的功能,grep重点是更容易阅读。

由于您没有说明输入是如何到达的,所以我假设是管道。适应文件输入是很简单的。

echo "(memberOf=CN=Group1)(memberOf=CN=Group2)(memberOf=CN=Group3)(memberOf=CN=Group4)" \
  | grep -Eo "[(]memberOf=CN=[^)]+" \
  | grep -Eo "[^=]+$"

这分两个阶段进行。

(memberOf=CN=something首先,我们在没有结束的情况下提取所有模式)——这对于我们的第二阶段很重要。这些都是“免费”的,这使得我们在第二步中的工作变得更容易。输入中任何不符合此模式的字符串都将被忽略,因此它也应该在所需字符串被埋藏在其他事物中的情况下工作。

接下来,我们匹配所有不是 的内容=,从行尾开始向后工作。这具有删除memberOf=CN=字符串部分的效果。

如果无法保证输入的大小写,请添加i到第一个 的标志中grep

需要注意的是:如果存在转义)=在您的CN值中,则这将无法按预期工作。

相关内容