我在 Linux 中有一个文件,其中包含如下行:
(memberOf=CN=Group1)(memberOf=CN=Group2)(memberOf=CN=Group3)(memberOf=CN=Group4)
我的目标是填充该行中存在的组列表,例如 -
Group1
Group2
Group3
Group4
答案1
与pcregrep
(P
erlC
兼容的R
正常E
表达grep
):
pcregrep -io1 '\(memberOf=CN=(.*?)\)'
或者直接使用perl
:
perl -lne 'print $1 while /\(memberOf=CN=(.*?)\)/gi'
(此处使用-i
或i
标志作为 LDAP 属性名称不区分大小写)。
如果您的grep
实现支持该-P
选项(就像 GNUgrep
在使用 PCRE 支持构建时所做的那样),那么它很可能也支持非标准选项-o
,您可以这样做:
grep -iPo '\(memberOf=CN=\K.*?(?=\))'
这里使用\K
and 的前瞻运算符表示(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"
答案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
值中,则这将无法按预期工作。