我正在清理DNS区域文件,以提取仅包含CNAME
和A
记录的源域,并消除所有注释,TXT
,MX
和SRV
记录。最重要的是,我想使这个过程自动化。
我设法创建了一个RegEx,该RegEx可用于在Sublime Text中执行“查找和替换”,并且需要进行几次迭代才能实现所需的结果。
让我们以以下示例DNS区域文件为例:
$ORIGIN example.com.
@ 3600 SOA ns1.p30.dynect.net. (
zone-admin.dyndns.com. ; address of responsible party
2016072701 ; serial number
86400 IN NS ns1.p30.dynect.net.
3600 IN MX 10 mail.example.com.
60 IN A 204.13.248.106
abc TXT "v=spf1 includespf.dynect.net ~all"
mail IN A 204.13.248.106
vpn IN TXT v=spf1 includespf.dynect.net ~all"
vpn2 IN MX v=spf1 includespf.dynect.net ~all"
webapp IN A 216.146.46.10
#webapp1 IN A 216.146.46.10
xyz IN CNAME example.com.
webapp IN SRV 216.146.46.11
;webapp2 IN A 216.146.46.11
第1步
将此用于“查找”
(^;.*)|(^#.*)|(^\$.*)|(^@.*)|(.*IN\h+MX.*)|(.*IN\h+TXT.*)|(.*IN\h+SRV.*)|(.*IN\h+NS.*)|(.*\h+TXT.*)|(\h.+)|(^[\n\r\h]+)
然后什么也不要替换。
这将清除区域文件,并仅显示指向CNAME
和A
记录的源域。
第2步
将以下内容用于“查找”
(.+$)
并用
替换\1.example.com
结果是带有源域的源域列表:
mail.example.com
webapp.example.com
xyz.example.com
我现在正尝试编写一个Python脚本,对给定的区域文件执行上述操作,并将其输出到.txt
文件中。
Wiktor Stribiżew是Stack Overflow的RegEx和Python之神,它帮助我编写了以下内容:
import re
regex = re.compile(r'^(?:\s+|[;#$@].*)|.*IN\s+(?:MX|TXT|SRV|NS).*|.*\s+TXT.*|\s.+')
with open('1.txt', 'r',encoding='UTF8') as dns:
with open('2.txt', 'w',encoding='UTF8') as output:
for line in dns:
if line.strip():
line = regex.sub('', line.strip())
if line:
output.write("{}.example.com\n".format(line))
不幸的是,脚本的输出是这样的:
zone-admin.dyndns.com..example.com
2016072701.example.com
60.example.com
mail.example.com
webapp.example.com
xyz.example.com
脚本不会忽略以空格开头的行。我在做什么错了?
答案 0 :(得分:2)
看起来您有两个错误,每个错误都会导致错误使用以空格开头的行。
第一个错误是在语句中
line = regex.sub('', line.strip())
会在将行传递到sub()
方法之前删除所有前导空格 。因此,正则表达式永远不会看到以空格开头的任何行。
要解决此问题,在调用strip()
方法之后,必须先调用sub()
方法:
line = regex.sub('', line).strip()
请注意,strip()
的全部原因是为了删除尾随的换行符,将line
设置为空字符串以忽略行。可以使用一个简单的测试来检查这一点,因为空字符串是虚假的。
作为替代方案,可以省略此调用,而可以修改正则表达式以删除换行符。 (这可以通过用.*
替换所有“跟踪” [\s\S]*
来完成。)
第二个错误是在您的正则表达式中,它仅匹配行的前导空白部分,而不是整个行。这将导致sub()
方法从本质上剥离前导空格!
Demo 1 1
regex = re.compile(r'^(?:\s+|[;#$@].*)|.*IN\s+(?:MX|TXT|SRV|NS).*|.*\s+TXT.*|\s.+')
↑_↑
|
only matches the leading white-space part, not the whole line
快速解决方案是向前推进非捕获组的结束括号:
Demo 2 1
regex = re.compile(r'^(?:\s+|[;#$@]).*|.*IN\s+(?:MX|TXT|SRV|NS).*|.*\s+TXT.*|\s.+')
↑ ↓
←
请注意,可以通过意识到可以在字符类内部移动空白元字符\s
来创建一个更简单的正则表达式,并且我们只需要检查该行的第一个字符即可:
Demo 3 1
regex = re.compile(r'^[\s;#$@].*|.*IN\s+(?:MX|TXT|SRV|NS).*|.*\s+TXT.*|\s.+')
最后,通过使用否定先行匹配而不匹配 not 指向CNAME或A记录的每条非注释,无前导空格行,可以实现进一步的简化,而不是彻底而详尽地指向非CNAME /非A记录的匹配行:
Demo 4 1
regex = re.compile(r'^(?:[\s;#$@]|(?!.*IN\s+[AC])).*|\s.+')
或者,如果您希望减少嵌套(加上一个较短的字符;-)):
Demo 5 1
regex = re.compile(r'^[\s;#$@].*|^(?!.*IN\s+[AC]).*|\s.+')
这是使用上面的最后一个正则表达式的代码的完整工作版本:
import re
regex = re.compile(r'^[\s;#$@].*|^(?!.*IN\s+[AC]).*|\s.+')
with open('1.txt', 'r',encoding='UTF8') as dns:
with open('2.txt', 'w',encoding='UTF8') as output:
for line in dns:
if line.strip():
line = regex.sub('', line).strip()
if line:
output.write("{}.example.com\n".format(line))
1 所有演示正则表达式均已进行了调整(最后一个空格元字符\s
已替换为空格)以允许多行标志被设置为用于显示所有替换完成的结果行(在“替换”框中)。这不会影响正则表达式的功能,因为测试字符串仅包含空格和换行符,而没有其他空格。