shell中sed学习之替换 \1 \2 \3的几点谜思

来源:互联网 发布:淘宝10元包邮网址 编辑:程序博客网 时间:2024/06/05 18:35

     今天在学习shell时,无意中碰到了这样一个脚本,其中有行用到了sed

 

echo “111(222)333" | sed 's/(\(.*\))(.*)/\2\2\2/'
     看完后,百思不得其解,因为本人目前只是对shell略懂皮毛,只知道基本的语法格式。然后就百度,终于明白了这是怎么一回事。

     按上面这个例子,在sed替换语句那里,有两个表达式,(\(.*\)) 和 ( .* ),其中前一个有时有转义符,有时没有,表示,有转义符时,表示正则表达式 POSIX 的基本正则表达式 ( BER )引擎的字符组,表示里面的内容会出现,如果里面有多个内容,则至少会出现一个,这里,里面是 .*,点通配符  . 表示可以匹配除换行符之外的任意单个字符,而 * 表示可以出现任意多次,包括依次都不出现。

    而前面没加转义符的一队双括号其实可以理解为一个标志,因为 echo 后面的语句有一对双括号,这是 sed 后的语句的双括号正好与之配对。所以,sed 后的替换语句中, ( \( .*\))表示的就是(222),而着后面的 (. *) 就是表示(222) 厚的所有内容。而后面的 \2表示的是这两个部分的第二个部分,即 (.*), 这里用到了三个 \2, 则会将这个部分重复三次,而前面的部分 111 没有影响,会直接输出,所以整个输出就是:

    111333333333。

    如果此处将  \2 改为 \1, 则会将 222 重复出现三次, 结果为 111222222222。


   当然,echo 语句中的括号我可以换成别的,如 111y222y333,那么,要得到相同的输出,则应该这么改写语句:

 

 echo "111y222y333" | sed 's/y\(.*\)y(.*)/\2\2\2/'

  两个括号及其以上得情况:

[root@mail root]# echo "no1=100(AAA) no2=100(BBB) no3=100(CCC)no4=(DDD)" | sed 's/\(.*\)(\(.*\)).*/\2/'

DDD
[root@mail root]# echo "no1=100(AAA) no2=100(BBB) no3=100(CCC)no4=(DDD)" | sed 's/.*(\(.*\)).*/\1/'
DDD
以上两句意义相同。
我们本想取到AAA,但是为什么取了DDD呢
正则表达式是有贪婪性的,它总是与最长的可能长度匹配,而且越是排在前面的通配符优先级越高。按照这样的原则取到得就是DDD了,那么我们应该如何取得AAA呢?

我们考虑如果在模式串中第一个.*中告诉sed这个.*是不能含有"("的,同时第二个.*中不能含有")"应该OK吧
试一试:
[root@mail root]# echo "no1=100(AAA) no2=100(BBB) no3=100(CCC)no4=(DDD)" | sed 's/[^(]*(\([^)]*\)).*/\1/'
AAA

以此类推,可以得到
[root@mail root]# echo "no1=100(AAA) no2=100(BBB) no3=100(CCC)no4=(DDD)" | sed's/[^(]*(\([^)]*\))[^(]*(\([^)]*\))[^(]*(\([^)]*\))[^(]*(\([^)
]*\))/\2/'
BBB
将2改为3就是CCC
将3改为4就是DDD

  上面这段是博客 点击打开链接,因为以目前所学的水平,还体会不了这段的意思,特摘来提醒自己,要继续学习,勤于思考,争取早日将这个问题弄懂。


   以上文章是写于2014-09-20 10:18,当时没弄懂上面一个问题,现在经过继续的学习,终于搞明白了。


      还是用上面 echo 语句输出的字串,当我本来想用 sed 输出 AAA 时,却还是输出 DDD。这是由于正则表达式的贪婪性。正如上面说的,正则表达式总是与最长的可能长度匹配,而且越是排在前面的通配符优先级越高,我们来看 sed语句:

 

sed 's/.*(\(.*\))/\1/'
    第一个通配符 .* 优先级较第二个高,它会匹配可能的最大长度,即匹配字串 ”no1=100(AAA) no2=100(BBB) no3=100(CCC)no4= “,所以当你输出 括号内匹配的第一个内容时,就会输出 DDD。怎么解决这个问题呢?请看下面分解:

    由于贪婪行,使得输出结果有误。让我们仔细看上面那个字串,不是有四个括号吗!我要输出 AAA,即是输出第一个括号的内容,所以只要我能匹配到第一个括号,那不就完美的解决了这个问题吗。实际上,也就是如此。

    那么,怎么来匹配第一个括号呢?

    看字串里,要想匹配括号内的数据,会用正则表达式  (\(.*\)),要想第一个呢?那么,我们只要在匹配括号内的内容时,让它前面不要出现  左双括号 “(”  不就可以了吗!所以,问题的关键就在这里。我们可以用下面的正则表达式来达到这一目的:

sed 's/[^(]*(\(.*\)).*/\1/'

    这样,输出就可以得到 AAA 了。

    那么,如果我想得到 BBB ,CCC,怎么办呢?

    这个问题就留给读者来回答吧!


0 0