[实践OK]Ansible里的后向引用124进行替换,以及正则匹配shell下写在一行和写YAML文件的正则区别,结合正则猫RegexBuddy/Patterns的正确用法。PHP正则匹配反斜杠''和美元'$'的方法以及Ansible单行shell交互和写入yml文件的正则不同写法的原因和对比成功实践及理解。

jackxiang 2018-6-27 11:23 | |
方便后面类似的需求,更快替换及测试,提高效率,如下:

ansible-playbook replace.yml -C -D



前置之1)Ansible的正则替换的模拟替换参数 -C -D,类似sed 的 -n 和 p结合只显示不真实替换,如下:
ansible-playbook aixiu_web.yml -e h=10.244.5.108 -C -D -t addhttpcdnsrcip

前置之2)正则被多重括号包起来的一个顺序和内容界定相当重要,它是从左到右数的一个\1\2\3的反向引用实践备忘:

上面1是最左边那个(,也就是所有的,第二个是匹配到的',它前面还有一些空行也被匹配上了的(                    '),所以上面显示有一些空的主要用来对新加的一行对齐,
第三个\3就是上一行去掉前和后的部分对于插入这行没有用(前无'后无,')(就是:"agent":"$http_user_agent"),第四个就是匹配到单独的一个(,'),
这个道理明白了也就对正则匹配出来的先后顺序有一个了解,再就是这个串特比有是\2里面的多个空格加一个单引号(                    '),
对于\n后面新加的一行对齐很重要,再就是\4也就是(,'),也是用来补充新加的一行少的部分,组成新一行起到了作用,见实践2:
                    ')                  <=== \2
"http_cdn_src_ip":"$http_cdn_src_ip"     <===新加的部分
,'                                       <=== \4    
上面三行构成了一个完整的和上面一样有N个空格打头的字符串:
                    '"http_cdn_src_ip":"$http_cdn_src_ip",'

backrefs参数:默认情况下,当根据正则替换文本时,即使regexp参数中的正则存在分组,在line参数中也不能对正则中的分组进行引用,除非将backrefs参数的值设置为yes。backrefs=yes表示开启后向引用,这样,line参数中就能对regexp参数中的分组进行后向引用了,这样说不太容易明白,可以参考后面的示例命令理解。backrefs=yes除了能够开启后向引用功能,还有另一个作用,默认情况下,当使用正则表达式替换对应行时,如果正则没有匹配到任何的行,那么line对应的内容会被插入到文本的末尾,不过,如果使用了backrefs=yes,情况就不一样了,当使用正则表达式替换对应行时,同时设置了backrefs=yes,那么当正则没有匹配到任何的行时,则不会对文件进行任何操作,相当于保持原文件不变。

原文:https://blog.csdn.net/dylloveyou/article/details/80698531

实践1)一行命令实现了正则替换,也就是Shell->Ansible的一个路径,它于直接定到yml里的正则写法不一样,看实践2作比较,原因:
写到文件里和一行的正则写法是不一样的,写一行涉及到终端传Shell的问题,而写到yaml的Ansible文件里则是python的交互,所以正则写法不大一样,如下:
shell交互:  regexp='((.*)(\"agent\":\"\\\$http_user_agent\")(.*))'
yaml交互:  regexp: '((.*)(\"agent\"\:\"\$http_user_agent\")(.*))'
比对发现:
一)\: 冒号在shell不需要转义,而在yaml文件里需要转义。
二)而shell里对$转义的右斜杠需要再加两个右斜杠一共三次,而yaml文件里轩一次也就行了,后面有描述这个$的问题。


三)有条件的替换,以防止出现替换时因为多次运行相同的Ansible脚本进而多次插入,注意when里面的变量加上单引号为字符串,否则会出现判断不准的问题:


实践2)实践发现假如要写到Yaml文件里,上面单独这一行放Shell里运行可以,但是放到yaml文件里是不行的,怎么办,重新修改调试Ok的文件版本如下所示:

TASK [将CDN透传过来的客户端访问出口IP写入Nginx日志] **********************************************************************************************************
--- before: /usr/local/nginx/conf/nginx.conf (content)
+++ after: /usr/local/nginx/conf/nginx.conf (content)
@@ -66,6 +66,7 @@
                     '"xff":"$http_x_forwarded_for",'
                     '"referer":"$http_referer",'
                     '"agent":"$http_user_agent",'
+                    '"http_cdn_src_ip":"$http_cdn_src_ip",'
                     '"status":"$status"}';

     access_log  /data/logs/nginx/access.log main;          #这一行显示冗余,并没有用,主要看+号。

changed: [10.244.25.77]



实践3)用ansible的insertafter实现:
如果用正则查到某行,在其后面写上也成用insertafter实现,但是这样据前面文章和实践就无法用这个\1\2这样的了,如果打开那个backrefs就需要regexp了,于是这样写实践是Ok的:

--- before: /usr/local/nginx/conf/nginx.conf (content)
+++ after: /usr/local/nginx/conf/nginx.conf (content)
@@ -66,6 +66,7 @@
                     '"xff":"$http_x_forwarded_for",'
                     '"referer":"$http_referer",'
                     '"agent":"$http_user_agent",'
+                     '"http_cdn_src_ip":"$http_cdn_src_ip",'
                     '"status":"$status"}';


实践4)进一步实践发现正则和insertafter混用也是可以的,
也就是说insertafter之后,再加一个regexp正则匹配出\1\2可用在line里,同时加上backrefs: yes,可行的,
如下实践也是能实现的,去掉之前的\1和\n即可,就在它后面插入即可,实践发现并没在后面插入,而是直接替换了,也就是说insertafter失效了,还得按实践2走才Ok,要不就老老实实的按实践3在后在插入,不要引入正则也成,引入正则就失去了insertafter的本来功能了:


$ansible-playbook iweb_regexp.yml -C -D -t insertafter

PLAY [insertafter with regexp] **************************************************************************************************************

TASK [insert after] *************************************************************************************************************************

--- before: /usr/local/nginx/conf/nginx.conf (content)
+++ after: /usr/local/nginx/conf/nginx.conf (content)
@@ -65,7 +65,7 @@
                     '"request_uri":"$request_uri",'
                     '"xff":"$http_x_forwarded_for",'
                     '"referer":"$http_referer",'
-                    '"agent":"$http_user_agent",'
+                    '"http_cdn_src_ip":"$http_cdn_src_ip",'
                     '"status":"$status"}';

     access_log  /data/logs/nginx/access.log main;



最后,基础研究,正则替换的基础知识:
对于斜杠来讲,在PHP里即使是单引号,它也是会和类似双引号里的$一样,有被转义:
'/\\\\/'
cat a.php


php a.php
\'

cat reg3.php  


来自微信群:是因为双引号的原因,\#在双引号里,就是\#,\\#在双引号里,就是\#,\\\#在双引号里,就是\\#。

之前http://jackxiang.com/post/6466/研究过反斜杠,没有研究过$,这次主要研究$。
"^-? \\d+$":这个正则表达式为什么会有两个反斜杠
这要分两步看
首先字符串中的\\被编译器解释为\
然后作为正则表达式,\d又被正则表达式引擎解释为元字符只匹配数字
正则表达式中匹配一个反斜杠要用四个反斜杠,为什么呢?

分析一下“\\\\”,第一个斜杠是转义符,第二个斜杠是斜杠本身,第三个斜杠是转义符,第四个斜杠是斜杠本身。
有2点要清楚:
1.字符串里面表示斜杠就需要两个斜杠如“\\”
2.正则表达式里的斜杠需要转意,是用“\\”标示。
这样就比较好解释:
我们先要表示正则表达式里面的斜杠“\\”,然后再用字符串表示出来。而这2个斜杠分别需要一个转义符,这样就成了4个斜杠在正则表达式里面表示一个斜杠。
From:https://my.oschina.net/airship/blog/411045

php实践如下:



拓展,在Ansible里是Python正则替换时遇到 $符号时会有三个斜杠(http://jackxiang.com/post/8857/),而为什么是三个斜杠呢?

最外层-a""双引,不看regexp的单引号。
reg.php


a.txt
123$ab


运行:php reg.php
会提示:PHP Notice:  Undefined variable: ab in /data/codesdev/testdemo/php/reg/reg.php on line 2,
这里的PHP用双引号里面的$会被PHP的解释器解释为一个变量的,如这儿便被认为$ab是一个变量,
用单引号就不存在这个问题,如果非要用单引号,就得转义那个\$,也就让PHP解释器认为这个串里的$仅仅是一个$符号。

三个斜杠的原因,第一个斜杠就是前面讲的双引号里面的$转义为非变量的$,仅仅是一个字符的$,外层双引号为最大,只要外层是引号,在里面加单引也没有用:
$ echo $a
123
$ echo "$a"
123
$ echo "\$a"
$a
$ echo "'\$a'"
'$a'
第二个斜杠是放在正则双引号里面的,因为这个$符号在正则里面看它在正则里的意思是行尾,我们需要认为它在正则里面还是一个$符号,不表示行尾,加第二个斜杠转义表示在正则里的$符号(preg_match"里面是正则的眼光看,这里以#号为正则范围),而这第二个斜杠它在双下号里又认为它是转义的一个斜杠,而不是真正的斜杠,要正的斜杠则要再加一个斜杠,于是有了第三个斜杠。



cat reg2.php


cat a2.txt
123\$abcdefg

#php reg2.php
int(4)
int(1)
array(1) {
  [0]=>
  string(1) "\"
}


RegexBuddy/Patterns的使用方法和来自:https://www.jb51.net/article/104895.htm的一个对照,如下:
test.php


正则猫RegexBuddy,Match-->
选个Python吧,能匹配的正则表达式:
<td>[0-9]{7,}<\\/td>\d\$$

Test:
1111111<td>2222222<\/td>3$

而上文里多的斜杠,从哪儿得出来呢?
Copy->CopyRegex as...->CopyRegex as PHP string
'<td>[0-9]{7,}<\\\\/td>\d\$$'
Copy->CopyRegex as...->CopyRegex as PHP '//' preg String
'%<td>[0-9]{7,}<\\\\/td>\d\$$%' ,替换为最左最右斜杠:
'/<td>([0-9]{7,})<\\\\/td>\d\$$/'   #正则猫的正则拷贝Copy结果,加上数字匹配的括号
'/<td>([0-9]{7,})<\\\\\/td>\d\\$/'  #PHP能成功运行并找到正确匹配结果的字符串正则,上面四个斜杠多两个斜杠,\d后面多一个斜杠。

也就是说放到PHP里面还得对斜杠再次转义,让正则认为它是真正的斜杠,而不是PHP里的转义符。

为何四个斜杠加两个斜杠?正则里的反斜杠转义为正则里的字符,但引号里的反斜杠还得转为PHP的正常认为的一个反斜杠,所以得加两个斜杠:
摘自:https://blog.csdn.net/xiaoxiaoniaoer1/article/details/7717669
   "\\"==> 反斜杠

在双引号内使用这些字符时,它们具有特殊的含义
转义字符代码  转义字符的含义
\ "  双引号
\ '  单引号
\ \  反斜杠
\ n  换行符
\ r  回车符
\ t  制表符
\ $  美元符号


实践发现放正则里的内容于外面是双引号单引号无关,只是双引号要转义,只要内容一样,匹配也一样的:
                                            

php reg.php
int(3)
int(1)
array(1) {
  [0]=>
  string(2) "$a"
}

然而,如果preg_match里的正则用单引号,那就匹配不出来了,如下:


php reg.php
int(3)
int(0)
array(0) {
}


cat reg.php  preg_match里的正则用单引号时,少一个斜杠就能匹配出来:
<?php
//$str = "123\$ab";
$str = '123$ab';
//$str = file_get_contents('a.txt');
var_dump(strpos($str,"\$a"));
var_dump(preg_match('#\\\$a#',$str,$rs));
var_dump($rs);

php reg.php  
int(3)
int(1)
array(1) {
  [0]=>
  string(2) "$a"
}


反过来说明啥?说明正则猫RegexBuddy默认是单引进行PHP的匹配,所以前面不用再加两个斜杠转义,证明下:

RegexBuddy采用双引号,也就无法匹配了:
php test.php
not match

得证:
$pattern = "%<td>([0-9]{7,})<\\\\/td>\d\$$%";      //not match
$pattern = "%<td>([0-9]{7,})<\\\\\\/td>\d\\$$%";  //matched
$pattern = '%<td>([0-9]{7,})<\\\\/td>\d\$$%';       //matched
也就是说RegexBuddy已经指明用单引号了,再回头看前面的:
Copy->CopyRegex as...->CopyRegex as PHP '//' preg String        <----它本来就是一个单引号'//' ,指明了,自己瞎搞要用双引号,双引号就再加一个对斜杠的转义即可!!!

回到背景里的这个Ansible,Ansible是用Python写的本质上是一样的道理:
  -C, --check           don't make any changes; instead, try to predict some
                        of the changes that may occur
  -D, --diff            when changing (small) files and templates, show the
                        differences in those files; works great with --check

                     '"referer":"$http_referer",'
                     '"agent":"$http_user_agent",'
                     '"http_cdn_src_ip":"$http_cdn_src_ip",'
+                    '"http_cdn_src_ip":"$http_cdn_src_ip",'
                     '"status":"$status"}';

     access_log  /data/logs/nginx/access.log main;

10.71.11.39 | SUCCESS => {
    "backup": "",
    "changed": true,
    "msg": "line replaced"
}

而前面用的是双引号,那么改成单引号怎么修改呢?和上面这个还不一样,它是写入到Client端后再运行的,有点不一样,用正则猫试试:
Match:
((.*)(\"agent\":\"\$http_user_agent\")(.*))
Test:
'"agent":"$http_user_agent",'
拷贝出来:
'/((.*)(\"agent\":\"\$http_user_agent\")(.*))/'




之前那个:
Match:
<td>([0-9]{7,})<\\/td>\d\$$
Test:
1111111<td>2222222<\/td>3$
拷贝出来:
'%<td>([0-9]{7,})<\\\\/td>\d\$$%'

发现没,那个斜杠是多到四个,但是后面的$并没有加斜杠,说明这只猫并不聪明,还得靠人人为把:
\$整成
\\$

而PHP里对这个 $前的斜杠并不敏感:
$pattern = '%<td>([0-9]{7,})<\\\\/td>\d\\$$%';
$pattern = '%<td>([0-9]{7,})<\\\\/td>\d\\$$%';
都能匹配出来:
#php test.php
Array
(
    [0] => Array
        (
            [0] => <td>2222222<\/td>3$
        )

    [1] => Array
        (
            [0] => 2222222
        )

)


总之,不能全信正则猫,实践得出单引号会少用斜杠,双引号就要多用斜杠。:




EOF

作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:https://jackxiang.com/post/9805/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!


最后编辑: jackxiang 编辑于2019-1-27 15:19
评论列表
发表评论

昵称

网址

电邮

打开HTML 打开UBB 打开表情 隐藏 记住我 [登入] [注册]