如何单独处理输入文件的块?



我正在尝试读取防火墙配置并使用Python操作它定义的对象,然后更改对象名称并将其导出到新的配置文件。输入示例:

edit "host1"
set subnet 10.0.0.10 255.255.255.255  
next
edit "host2"
set subnet 10.0.0.11 255.255.255.255 
next

读取文件时,我需要一种方法来遍历"edit [...]"的每个实例,并设置以下属性(可以有多个(。本质上,包含"编辑"的行表示新对象的开始,而"下一个"表示该对象的结束。目标是检查每个对象的内容并根据它的内容对其进行重命名,基于前面的示例,输出将是:

edit "10.0.0.10"
set subnet 10.0.0.10 255.255.255.255  
next
edit "10.0.0.11"
set subnet 10.0.0.11 255.255.255.255 
next

操作文本本身应该不是什么大问题,但我目前一直在寻找一种方法来单独处理配置的每个块。

我考虑过将输入转换为XML,然后使用ElementTree,但我希望有一种更优雅的方法,而没有由于缺乏编程经验而看不到的额外步骤。

提前感谢您对如何解决此问题的任何意见。

递归下降解析器时间!

如果这些配置文件真的那么简单,你可以使用以下语法:

start: block*
block: EDIT HOSTNAME rule* NEXT
HOSTNAME: /"[^"]"/
rule: SET SUBNET IP IP
IP: /d+.d+.d+.d+/

然后解析器将如下所示。它使用set规则中的主机自动重写所有edit块。

import re

class Parser:
def __init__(self, tokens: list):
self.tokens = tokens
def _next(self) -> str:
token = self.tokens[0]
del self.tokens[0]
return token
def _peek(self) -> str:
return self.tokens[0]
def consume(self, value: str) -> str:
token = self._next()
if token == value:
return token
raise SyntaxError(f"Could not consume {value!r} given {token!r} + {self.tokens}")
def consume_regex(self, regex) -> str:
token = self._next()
match, = regex.fullmatch(token).groups()
return match
def parse(self):
edits = self.parse_start()
assert not self.tokens
return edits
def parse_start(self):
"start: edit*"
edits = []
while self.tokens:
edit = self.parse_edit()
edits.append(edit)
return edits
def parse_edit(self) -> str:
"edit: EDIT HOST rule NEXT"
HOST_REGEX = re.compile(r'^"([^"]*)"$')
_EDIT = self.consume('edit')
_HOST = self.consume_regex(HOST_REGEX)
_rule = self.parse_rule()
_NEXT = self.consume('next')
# REWRITE HERE!
rewritten = f'''
{_EDIT} "{_rule['IP']}"
{_rule['type']} {_rule['subnet']} {_rule['IP']} {_rule['mask']}
{_NEXT}
'''.strip()
return rewritten

def parse_rule(self) -> dict:
"rule: SET rule_set"  # other rules can be added
tok = self._peek()
if tok == 'set':
return self.parse_rule_set()
raise SyntaxError(f'Unknown rule {tok!r}')
def parse_rule_set(self) -> dict:
"rule_set: SET SUBNET IP MASK"
# this regex doesn't check the validity of the IP address,
# but it's good enough here
IP_REGEX = re.compile(r"^(d+.d+.d+.d+)$")
_SET = self.consume('set')
_SUBNET = self.consume('subnet')
_IP = self.consume_regex(IP_REGEX)
_MASK = self.consume_regex(IP_REGEX)
return {'type': 'set', 'subnet': _SUBNET, 'IP': _IP, 'mask': _MASK}

if __name__ == '__main__':
data = '''
edit "host1"
set subnet 10.0.0.10 255.255.255.255  
next
edit "host2"
set subnet 10.0.0.11 255.255.255.255 
next
'''
ret = Parser(data.split()).parse()
print('n'.join(ret))

输出:

$ python3 test.py
edit "10.0.0.10"
set subnet 10.0.0.10 255.255.255.255
next
edit "10.0.0.11"
set subnet 10.0.0.11 255.255.255.255
next
$ 

相关内容

最新更新