这个堆栈溢出Q&;A让我思考在需要查找的情况下用GNU sed构建解决方案的各种方法。
这里只介绍单行(以换行符作为行分隔符)处理。还显示了使用grep-P或Perl的等效Lookround语法,以供比较。模式空间中存在多行和/或ASCII NUL字符的情况留作练习。
在这里,您只需要决定是否必须匹配输入行。SED支持对{}中的命令进行分组,只有在匹配筛选条件时才应执行这些命令。条件可以通过添加!性格。通过这种方式,您可以模拟多个正向和/或负向环视条件的链接。
$cat items.txt 1,2,3,4 apple=50;per KGA,b,c,d;foo xyz3#包含数字字符后跟a;character#Lookround isn';此处不需要的字符的筛选行#与:grep';[0-9].*;';或grep-p';\d(?=.*;)';$sed-n&39;/[0-9]。*;/p';items.txtapple=50;Per kg#筛选任意顺序同时包含数字和;字符的行#Same as:grep-P';^(?=.*;).*\d';$sed-n';/;//{/[0-9]/p}';items.txtapple=50;Per kg;foo xyz3#筛选同时包含数字和;字符的行#,但如果该行还包含字符a#Same as:grep-P';^(?!.*a)(?=.*;).*\d';$sed-n';/a/!{/;/{/[0-9]/p}}';items.txt;foo xyz3。
对于某些情况,像前面的示例那样的多个条件检查是不够的。例如,只要购物车不在后面的行中出现,就可以过滤包含PAR的行。出现在队伍前面的购物车应该不会影响结果。在这种情况下,您可以首先更改输入行,以便在出现Cart的地方添加换行符,然后构造一个条件,使其依赖于换行符,而不是Cart。如果找到匹配项,请删除所有换行符,然后打印该行。
$s=&39;par购物车备用车公园城市#同:grep-p&39;par(?!.*cart)';$ECHO";$s";|sed-n&39;s/cart/\n&;/g;/par[^\n]*$/{s/\n//g;p}';par购物车备用购物车公园城市。
换行符是默认逐行处理的安全字符,因为sed会将其从模式空间中删除。如果您正在处理包含换行符的模式空间(例如:-z选项、N命令等),那么只要您知道输入数据中不存在某个字符,您仍然可以执行此技巧。
在上一节中,您了解了如何使用换行符修改输入行,以便更容易地构造Lookround条件。这个小窍门在替换时也派上了用场。但是,对于搜索和替换情况,您还需要模拟环视的零宽度特性。要实现这一点,可以使用t命令构造一个循环,只要找到匹配项就执行替换。有关GNU sed中分支命令的更多详细信息,请参阅我关于控制结构的章节。
这里有一个循环的例子。目的是递归地从给定输入中删除FIN。
#手动重复,假设计数已知$echo';s/fin//#39;cofing$echo';s/fin//#39;|sed';s/fin//;s/';cog#:只要替换成功,循环就会用标签';loop';#tloop跳转到标签';loop';s/fin';cog';s/fin&/;s//';s/#39;cog#:LOOP用标签';loop';#tloop将跳到标签';loop';|sed';:循环s/fin//;tloop';齿轮。
有些情况只能在首先满足条件的情况下才能通过执行替换来解决。请注意,这里的{}分组是可选的。
#与:perl-ne';Print if s/^(?!;).*?\K[,].*//';$sed-n';/^;/!S/[,].*//p';items.txt 1 Apple=50 a。
仅当foo后面没有数字字符时,才将foo更改为[baz]。请注意,字符串末尾的foo也满足此断言。Foofoo有两个匹配项,因为断言本质上是零宽度的,即它不使用字符。这里,第一步是在foo和数字字符之间插入换行符。然后将所有foo更改为[baz],只要它在字符串的末尾,或者如果它后面没有换行符。循环结束后,删除所有换行符。
嘿,食物!Foo42 Foo5 foofoo';#等同于:perl-pe';s/foo(?!\d)/[baz]/g';$echo";$s";|sed-E';s/(Foo)([0-9])/\1\n\2/g;:a s/foo([^\n]|$)/[baz]\1/;ta;s/\n//g';嘿[baz]d!FOO42[Baz]T5[Baz][Baz]。
仅当foo前面没有_CHARACTER时,才将foo更改为[baz]。字符串开头的foo也是匹配的。
$s=';foo_foo 42foofoo';#等同于:perl-pe&39;s/(?<;!_)foo/[baz]/g';$echo";$s";|sed-E';s/(_)(Foo)/\1\n\2/g;:a s/(^|[^\n])foo/\1[baz]/;ta;s/\n//g';[Baz]_foo 42[Baz][Baz]
只要s字符稍后不出现在输入中,就用[xyz]替换PAR。这假设断言不与搜索模式冲突,例如,s不会与PAR冲突,但如果它是r和PAR,则会受到影响。
$s=';par备件方';#等同于:perl-pe';s/par(?!.*s)/[xyz]/g';$echo";$s";|sed-E';s/s/&;\n/g;:a s/par([^\n]*)$/[xyz]\1/;ta;s/\n//g';par s[xyz]e[xyz]t[xyz]。
将CSV输入的所有空字段替换为NA(假设没有嵌入逗号、换行符等)。
$s=';,1,2,3,#相同于:perl-lpe';s/(?<;![^,])(?![^,])/na/g';$echo";$s";|sed-E&39;:a s/,/,na,/g;ta;s/^,/na,/;S/,$/,NA/';NA,1,NA,NA,2,3,NA,NA,NA。
使用[]将字段括起来,CSV输入的第一个和最后一个字段除外(假设没有嵌入逗号、换行符等)。使用正向环视仿真时,修改后的字符串可能会继续满足匹配条件,从而导致无限循环。在本例中,字段本身可能包含[]字符,因此不能使用它们来防止无限循环。换行符技巧又派上用场了。
$s=';1,t[w]o,[3],f[ou]r,5';#等同于:perl-pe';s/(?<;=,)[^,]+(?=,)/[$&;]/g';$echo";$s";|sed-E&39;:a s/,([^,\n]+),/,\n[\1],/g;ta;S/\n//g';1,[t[w]o],[[3]],[f[ou]r],5。
在单词边界处添加空格,但不能在字符串的开头或结尾处添加空格。此外,如果已经存在空格,请不要添加。在这里,空格字符上的被否定的字符类足以模拟断言。
$s=';Total=num1+35*42/num2';#等同于:perl-lpe';s/(?<;=[^])\b(?=[^])//g';$ECHO";$s";|sed-E&39;:a s/([^])\b([^])/\1\2/;ta;';Total=num1+35*42/num2
只要部分在该行后面作为一个完整的单词出现,就用[xyz]替换par。这里,修改后的字符串本身的性质防止了无限循环的可能性。
$s=';PAR备件方';#等同于:perl-pe';s/par(?=.*\bpart\b)/[xyz]/g';$echo";$s";|sed-E&39;:a s/par(.*\bpart\b)/[xyz]\1/;ta';[xyz]s[xyz]e部件方。
分支命令和输入的一些创造性预处理可以组合在一起,以模拟sed中的Lookround断言。考虑到Unix实用程序sed是完全图灵的,这也许并不令人惊讶。现在,请原谅,我将忙于在stackoverflow/unix上获取积分。此边缘案例的stackexchange;)