使sed替换只有精确的字符串

时间:2011-07-26 15:39:48

标签: regex linux bash sed preg-match

我有一个如下的css文件:

    #layout.one-column  #menu-secondary{background: #3c3c3c; height: 20px; font-family: 'Trebuchet MS'; font-weight: bold; font-size: 15px; padding: 10px;}     
    #layout.one-column  #menu-secondary a {color: #FFF; text-decoration: none;}
    #layout.one-column  #menu-secondary ul {}   
    #layout.one-column  #menu-secondary ul li {display: block; height: 30px; float: left; margin: 0 20px 0 0;}  
    .ofr h2 {font-size: 17px; height: 35px; margin: 0 10px 10px 10px;}  
    .ofr h2 a {color: #2a2a2a; text-decoration: none;}      
    #layout.one-column  #menu-secondary ul li.active {background: url(../img/selected.gif) no-repeat bottom center;}
    #layout.one-column  #menu-secondary ul li a {display: block; float: left; padding: 0 10px;}     
    #layout.one-column  #menu-secondary ul li a:hover {text-decoration: underline;}  

正如你可以看到它开头的每一行都有标签/偶然空格,字符串以.whatever /#whatever开头。我编写了一个小写的脚本,其中包含:

find css/myCSS.css -name "*.css" -type f -exec sed -i "s/\<$pattern\>/$replacer/g" {} \;

其中$pattern可以是#layout$replacer可以是#LAYOUT。我想做的是,如果字符串是

,那么以错误的方式做错,就是将#layout替换为#LAYOUT
  • 相等(blank spaces/tabs before and after the $pattern
  • 相等(blank spaces/tabs just before the $pattern)后跟dot加上任何(#pattern.whatever
  • 相等(blank spaces/tabs just before the $pattern)后跟#加上任何(#pattern#whatever
  • 喜欢#whatever.pattern或#whatever#pattern(blank spaces/tabs just before the #whatever and after #pattern)。

我希望我现在做到了,cristal clear :)

以下是一些示例,每行都应该替换#pattern或.pattern:

#pattern     <- blank spaces/tabs before and after the string  
#pattern.bar <- blank spaces/tabs before #pattern  and after .bar  
.pattern#bar <- blank spaces/tabs before .pattern  and after #bar  
#foo.pattern <- blank spaces/tabs before #foo and after .pattern  
.foo#pattern <- blank spaces/tabs before .foo and after #pattern  
.pattern     <- blank spaces/tabs before and after the string   

我一直在尝试用sed做这件事,但我无法通过,并认为我可以为每天与sed工作的人“轻松”。再次感谢:)

3 个答案:

答案 0 :(得分:2)

如果要根据需要重新定义单词边界,则需要枚举它们。一种方法是,抓住边界模式并将其追加到最后:

echo "well #menu not #menu-foo #menu" | sed -r 's/#menu([ \t\n\r.!?,]|$)/#MENU\1/g'
well #MENU not #menu-foo #MENU

|$是抓取输入大小写的文件/结尾。

我仍然不知道领先#的作用,但我想你可以应用这个想法到目前为止,如果你需要像\ 1MENU \ 2这样的第一个分隔符模式。

更新28.07,23:45:

  • 相等($ pattern前后的空白/制表符) [ \t]pattern[ \t]
  • 相等($ pattern之前的空格/制表符)后跟点加上任何(#pattern.whatever) [ \t]pattern.[^ \t]详尽地描述“无论什么”会更好。额外的点,是 - 允许 - 我们如何识别“什么”结束?空白?
  • 相等($ pattern之前的空白/制表符)后跟#plus what(#fode#whatever) [ \t]pattern#[^ \t]好的,就像上面一样,只是哈希而不是点。
  • 喜欢#whatevers.pattern或#whatever #pattern(#whatever和#pattern之后的空白/制表符)。 [ \t]#[^ \t].pattern[ \t][ \t]#[^ \t]#pattern[ \t]

没有。 2和3几乎相同。如果我们的意思是A或B,我们可以简单地形成一个组[#。]。在组内部,我们不需要掩盖点,因为作为小丑的点在组中没有任何意义。

没有。因此,组合2和3是

[ \t]pattern[#.][^ \t][ \t]

但是!你不用'什么'做任何事情。无论是什么,它都没有改变。所以我们添加#和。只是到分隔符列表(空白和制表符)并返回它们(或空白或制表符),无论它们是什么:

[ \t]pattern([#. \t])

一个简单的测试:

echo "well #menu not #menu-false #menu.dot #menu#hash" \
| sed -r 's/[ \t]#menu([#. \t])/ #MENU\1/g' 
well #MENU not #menu-false #MENU.dot #MENU#hash

这会修改#Menu前面的东西,无论是空白还是制表符,总是空白。如果需要,我们也可以捕获它。

| sed -r 's/([ \t])#menu([#. \t])/\1#MENU\2/g' 

但最后一条规则是什么,第4号,其中'无论'是什么导致'模式'?我们可以结合使用点和哈希:

[ \t]#[^ \t][.#]menu[ \t]

将这种情况合并到我们的正则表达式中将允许#foo #pattern #bar。那变得越来越复杂了。我们最好开始一个全新的命令:

s/([ \t]#[^ \t]+[.#])menu[ \t]/\1MENU /g'

可附加';'在上一个之后:

| sed -r 's/[ \t]#menu([#. \t])/ #MENU\1/g;s/([ \t]#[^ \t]+[.#])menu[ \t]/\1MENU /g'

所以我想我解决了你的4个规则,但顶部的例子只解决了其中的两个规则。并且您的尝试再次包括\<\>,这只会令人困惑。

这是我的自制示例,包括规则4的案例:

echo "well #bar.menu and #foo#menu #menu not #menu-false #menu.dot #menu#hash" \
| sed -r 's/[ \t]#menu([#. \t])/ #MENU\1/g;s/([ \t]#[^ \t]+[#.])menu[ \t]/\1MENU /g'

well #bar.MENU and #foo#MENU #MENU not #menu-false #MENU.dot #MENU#hash

答案 1 :(得分:1)

更新2

好的,您需要匹配以#.开头并且是有效的CSS标识符的整个单词,然后可能以CSS链或空格结尾。它们也可能在CSS链的末尾?

sed -i "s/\(\s+|[#.][a-z_][a-z0-9_-]*\)#pattern\(\s+|[#.:]\)/\1#PATTERN\2/"

这很丑陋,并且已经完成了所有内容。我检查了CSS规范,以确保我有适合选择器IDENT的模式。由于伪选择器,终端组中有:

OLDER STUFF

\b对您不起作用(因为您认为#menu-foo是单个项目,\ b将其视为四个# menu - foo。)

在我们为您提供帮助之前,您需要更清楚地了解您认为“单词突破”的内容。至少你可以尝试这样的sed,如果空间中断是你认为足够的:

sed -i "s/\(\s\)#menu\(\s\)/\1#MENU\2/"

或者,您将必须指定分词包含的内容。而不是你可能需要\(^|[\s"']\)作为开头,而某些东西对于最终条件来说真的很难看。

根据您的评论,如果您关心的每个令牌都在HTML标记之间,那么您可以执行以下操作。如果您不希望不区分大小写,请将-i从sed行中取出。此时,我唯一的问题是你的数据中是否还有换行符。是否所有HTML都在一个文本行上?

sed -i "s/>#menu</>#MENU</"

或者,发烧友并包括可能的换行符:

sed -i "s/\(^|>\)#menu\($|<\)/\1#MENU\2/"

我们可能需要样本数据来超越这个......

答案 2 :(得分:1)

根据问题重写重写。警告,这里播放一些引用游戏:

pattern="layout"
replace="FOO"

sed 's/\([ \t#.]\)'"$pattern"'\([ \t#.]\)/\1'"$replace"'\2/g'  << EXAMPLE

 #layout  #layout.whatever #layout#whatever
 #whatever.layout #whatever#layout
 .layout .layout.whatever .layout#whatever
EXAMPLE

产生

 #FOO  #FOO.whatever #FOO#whatever
 #whatever.FOO #whatever#layout
 .FOO .FOO.whatever .FOO#whatever