使用sed对模式的一部分求反

时间:2019-05-16 18:12:54

标签: regex bash sed regex-negation regex-group

我想创建一个模式,该模式将找到与该模式匹配的第一个字符串并将其替换:

sed -r '0,/^[a-z0-9]* (ALL=\(ALL\) ALL)/s//abc \1\n&/'

我的输入是一个/etc/sudoers文件。

我想将^[a-z0-9]*更改为/^ALL/!之类的内容,但对我来说不起作用。

3 个答案:

答案 0 :(得分:1)

我的猜测是您想从字符串中删除abc,此表达式可能会帮助您这样做:

([^abc]*)(.*)

enter image description here

#!/bin/bash
STRING="(ALL=(ALL) ALL)/s//abc"
MATCH="$(sed 's/\([^abc]*\)\(.*\)/\1/' <<< $STRING)"
echo $MATCH

输出

(ALL=(ALL) ALL)/s//

RegEx

如果这不是您想要的表达式,则可以在regex101.com中修改/更改表达式。

RegEx电路

您还可以在jex.im中可视化您的表达式:

enter image description here

答案 1 :(得分:1)

替换文件中的首次出现

如果我理解您的问题,并且您想在文件中找到与^[a-z0-9]+ ALL=(ALL) ALL匹配的第一个字符串,并使用扩展的正则表达式将^[a-z0-9]+ ALL替换为^ABC之类的内容,您可以使用:

sed -r '0,/[a-z0-9]+/s/^[a-z0-9]+ ALL(=\(ALL\) ALL.*$)/^ABC\1/'

查看/etc/sudoers组成员具有sudo访问权限的wheel中未注释的行,您将:

$ noc /etc/sudoers
root ALL=(ALL) ALL
%wheel ALL=(ALL) ALL
%wheel ALL=(ALL) NOPASSWD: ALL

因此,将[a-z0-9]+ ALL=(ALL) ALL中的第一个字符串替换为/etc/sudoers中的^ABC=(ALL) ALL,您将:

$ noc /etc/sudoers | sed -r '0,/[a-z0-9]+/s/^[a-z0-9]+ ALL(=\(ALL\) ALL.*$)/^ABC\1/'
^ABC=(ALL) ALL
%wheel ALL=(ALL) ALL
%wheel ALL=(ALL) NOPASSWD: ALL

按评论编辑

在评论中,您说要查找“ <string> ALL=(ALL) ALL,其中string可以是所有内容,但不能是'ALL'”。由于sed不支持超前查找或环顾四周,因此,最好的办法是在行首明确不匹配ALL。意味着您要查找以^[^A][^L][^L]开头的行。尽管这在某种程度上有限制,但它会查找以"ALL"开头的行,但这可能是您唯一的选择。喜欢:

sed -r '0,/^[^A][^L][^L]/s/^[^A][^L][^L][^ ]+( ALL=\(ALL\) ALL.*$)/^ABC\1/'

再次以sudoers文件为例,并添加您不想匹配的ALL ALL=(ALL) ALL,例如

$ cat sudoers
ALL ALL=(ALL) ALL
root ALL=(ALL) ALL
%wheel ALL=(ALL) ALL
%wheel ALL=(ALL) NOPASSWD: ALL

现在应用表达式来定位<string>中第一个ALL而不是<string> ALL=(ALL) ALL,您将得到:

$ sed -r '0,/^[^A][^L][^L]/s/^[^A][^L][^L][^ ]+( ALL=\(ALL\) ALL.*$)/^ABC\1/' sudoers
ALL ALL=(ALL) ALL
^ABC ALL=(ALL) ALL
%wheel ALL=(ALL) ALL
%wheel ALL=(ALL) NOPASSWD: ALL

替换行中出现的编号

如果要替换一行中的出现。使用扩展的正则表达式,使用s/find/replace/n(其中n=1,2,3..是要替换的find模式的出现),您可以将第一个出现的替换为,例如

sed -r 's/[0-9a-z]+/cde456/1'

例如:

$ echo "abc123---abc123---abc123" | sed -r 's/[0-9a-z]+/cde456/1'
cde456---abc123---abc123

或者要替换第二次出现的情况,您可以使用:

$ echo "abc123---abc123---abc123" | sed -r 's/[0-9a-z]+/cde456/2'
abc123---cde456---abc123

基本正则表达式可以使用相同的匹配项,只是您没有'+'(匹配一个或多个匹配项),是否必须更改find模式,例如

sed 's/[0-9a-z][0-9a-z]*/cde456/1`

在不使用扩展正则表达式的情况下替换所需的出现。

答案 2 :(得分:0)

我了解,如果ALL子字符串之前的部分中有ALL=(ALL) ALL,则您要避免匹配模式的第一次出现。

使用

sed -i '/^.*ALL.* ALL=(ALL) ALL/b;s/^.* \(ALL=(ALL) ALL\)/abc \1\n&/;T;:a;n;ba' file

如果您没有GNU sed,则可以尝试

sed -i '' -e '/^.*ALL.* ALL=(ALL) ALL/b' -e 's/^.* \(ALL=(ALL) ALL\)/abc \1\n&/' -e ':a;n;ba' file

注释

  • /^.*ALL.* ALL=(ALL) ALL/b;-查找在ALL之前有ALL=(ALL) ALL的行,并停止处理该行
  • s/^.* \(ALL=(ALL) ALL\)/abc \1\n&/执行替换操作(注意.*将匹配任意0个或更多字符,并尽可能匹配)
  • T-如果未成功,则结束对该行的处理
  • :a;n;ba-使sed只需读取并打印其余行到文件末尾的循环。