[实践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
)
)
总之,不能全信正则猫,实践得出单引号会少用斜杠,双引号就要多用斜杠。: