使用SED替换组匹配

时间:2017-12-01 00:30:20

标签: regex bash sed

我有一个简单的正则表达式,可以为双引号中包含的任何分号创建组匹配。我试图在Mac OS X上使用sed用'SEMICOLO'替换分号...但是,它不起作用..:/

这是我试图使用的命令:

sed -i.bu "s|.*?(;).*?|SEMICOLON|g" output/html/index.html

结果是没有任何东西匹配,也没有任何东西被替换。

期望的行为:

输入

"The man sat; the man cried;" cats; dogs;

输出

"The man satSEMICOLON the man criedSEMICOLON" cats; dogs;

更新: 谢谢大家的帮助。所以我的例子并不是很好。我真的需要把一个javascript文件压缩到一行,并确保每个js语句都有自己的行。问题是javascript主要是翻译文本,所以试图制作一个简单的正则表达式,在每个;之后插入换行很困难,因为如果分号在引号中,我显然不希望添加换行符。

长话短说......我意识到我正在尝试重新发明轮子,并决定使用js-beautifypretty print文件。它做的比我需要的多一点......但它现在是最好的解决方案。

再次感谢!

2 个答案:

答案 0 :(得分:2)

让我们将其作为测试文件:

$ cat file
"The man sat; the man cried;" cats; dogs;
1; 2; "man;"; 3; ";dog";

尝试使用此sed命令:

$ sed -E ':a; s/^(([^"]*"[^"]*")*[^"]*"[^"]*);/\1SEMICOLON/; ta' file
"The man satSEMICOLON the man criedSEMICOLON" cats; dogs;
1; 2; "manSEMICOLON"; 3; "SEMICOLONdog";

工作原理:

  • :a

    这会创建一个我们稍后可以参考的标签a

  • s/^(([^"]*"[^"]*")*[^"]*"[^"]*);/\1SEMICOLON/

    这将替换SEMICOLON中双引号内的最后一个;。让我们更详细地看一下^(([^"]*"[^"]*")*[^"]*"[^"]*);

    1. ^匹配字符串的开头。

    2. ([^"]*"[^"]*")*从该行的开头匹配任意数量的完整引用字符串。

      因为,在sed中,正则表达式是贪心(更确切地说,最左边最长),这将尝试匹配尽可能多的完整引用字符串。

    3. [^"]*"[^"]*;匹配完整引用字符串后面的任何非引号(如上所示),跟随下一个引号字符,后跟任意数量的非引号字符,后跟;

    4. 由于上述正则表达式减去最终;本身就在parens中,因此将其保存为组1.我们将匹配的文本替换为组1,然后替换为SEMICOLON。

    5. ta

      如果最后一个命令导致替换(换句话说,我们发现需要替换;),则跳回标签a并重复。

讨论

让我们考虑一下:

sed "s|.*?(;).*?|SEMICOLON|g" 

在Python和其他地方,.*?是非贪婪的匹配。然而,Sed没有这样的概念。就此而言,默认情况下,sed使用基本正则表达式(BRE),其中?仅表示文字问号。

另外,将sed命令放在双引号中会有麻烦,因为这会邀请shell修改它。

因此,由于BRE已经过时,让我们(1)使用-E开关切换到扩展正则表达式(ERE),(2)将命令放在单引号中,以及(3)将.*?更改为.*

$ sed -E 's|.*(;).*|SEMICOLON|g' file
SEMICOLON

兼容性说明:如果您使用的是非常旧的Linux系统,则可能需要将-E替换为-r。)

.*(;).*匹配行上最后一个分号的所有内容,后跟分号,后跟最后一个分号后面的内容。换句话说,如果该行包含分号,则.*(;).*匹配整行。这就是为什么输出只是SEMICOLON

此外,(;)匹配分号并将其保存在组1中。由于我们从不在任何地方使用组1,因此这对我们没有任何作用。我们会得到相同的结果:

$ sed -E 's|.*;.*|SEMICOLON|g' file
SEMICOLON

如果我们移除.*,则会替换每个;

$ sed -E 's|;|SEMICOLON|g' file
"The man satSEMICOLON the man criedSEMICOLON" catsSEMICOLON dogsSEMICOLON

如果我们想要替换第一个引用字符串中的最后一个;,我们可以使用:

$ sed -E 's|^([^"]*"[^"]*);|\1SEMICOLON|g' file
"The man sat; the man criedSEMICOLON" cats; dogs;

如果我们想要替换该行上任何带引号的字符串中的所有;,那么我们将返回顶部的命令。

跨越行的字符串

让我们考虑一个跨越2行的字符串的测试文件:

$ cat file2
"man;" cat "dog
;"; ";man";

如果你有GNU sed:

$ sed -Ez ':a; s/^(([^"]*"[^"]*")*[^"]*"[^"]*);/\1SEMICOLON/; ta' file2
"manSEMICOLON" cat "dog
SEMICOLON"; "SEMICOLONman";

一般来说任何POSIX sed:

$ sed -E 'H;1h;$!d;x; :a; s/^(([^"]*"[^"]*")*[^"]*"[^"]*);/\1SEMICOLON/; ta' file2
"manSEMICOLON" cat "dog
SEMICOLON"; "SEMICOLONman";

答案 1 :(得分:1)

sed是简单的s / old / new就是全部。有任何awk:

$ awk 'match($0,/"[^"]+"/) {
    str = substr($0,RSTART,RLENGTH)
    gsub(/;/,"SEMICOLON",str)
    $0 = substr($0,1,RSTART-1) str substr($0,RSTART+RLENGTH)
} 1' file
"The man satSEMICOLON the man criedSEMICOLON" cats; dogs;

假设您实际上希望引用字符串中的所有分号都以相同的方式处理。如果没有,无论你想做什么都是一个简单的调整,例如如果您希望删除cried后的最后一个分号,而不是如示例输出中所示替换:

$ awk 'match($0,/"[^"]+"/) {
    str = substr($0,RSTART+1,RLENGTH-2)
    sub(/;$/,"",str)
    gsub(/;/,"SEMICOLON",str)
    $0 = substr($0,1,RSTART) str substr($0,RSTART+RLENGTH-1)
} 1' file
"The man satSEMICOLON the man cried" cats; dogs;
相关问题