Разбор dhcpd.conf с textX
Я использую https://github.com/igordejanovic/textX для анализа dhcpd.conf
файл (нет, https://pypi.org/project/iscconf/ не работает для меня, он падает на моем dhcpd.conf
файл), специально распаковывать хосты с фиксированными адресами.
Записи похожи на:
host example1 {
option host-name "example1";
ddns-hostname "example1";
fixed-address 192.168.1.181;
}
host example2 {
hardware ethernet aa:bb:ff:20:fa:13;
fixed-address 192.168.1.191;
option host-name "example2";
ddns-hostname "example2";
}
Код:
def get_hosts(s):
grammar = """
config: hosts*=host ;
host: 'host' hostname=ID '{'
(
('hardware ethernet' hardware_ethernet=/[0-9a-fA-F:]+/';')?
'fixed-address' fixed_address=/([0-9]{1,3}\.){3}[0-9]{1,3}/';'
('option host-name' option_host_name=STRING';')?
('ddns-hostname' ddns_hostname=STRING';')?
)#
'}'
;
"""
mm = metamodel_from_str(grammar)
model = mm.model_from_str(s)
for host in model.hosts:
print host.hostname, host.fixed_address
Теперь я не могу разобрать весь dhcpd.conf
с этой грамматикой (очевидно, я получаю синтаксические ошибки, поскольку в файле так много других элементов, которые грамматика не учитывает); с другой стороны, я не хочу строить полную грамматику для этого файла, так как мне нужно только извлечь определенный тип записей хоста.
Я, конечно, могу извлечь только записи хоста с помощью регулярных выражений и разобрать их отдельно, но мне интересно, есть ли способ сделать textX
только извлекать host
записи из файла и игнорировать остальное содержимое?
1 ответ
Автор textX здесь. Я не частый посетитель ТАК:). Вы можете поиграть с регулярными выражениями и заглядывать в регулярные выражения, чтобы использовать нежелательный контент. Вот полный пример, который правильно обрабатывает промежуточный текст, даже если есть ключевое слово host
, правило config
сначала потреблять символ, если нет слова host
вперед, и это повторяется из-за нуля или более оператора. Когда мы получим слово host
мы пытаемся сделать матч host
править один или несколько раз и собрать все хост-объекты, если правило не выполнено хотя бы один раз (обратите внимание на использование +=
) мы употребляем слово host
и повторите процесс. Это, вероятно, может быть сделано лучше (более производительным), но вы поняли идею. При выполнении такого рода вещей полезно знать, что textX по умолчанию использует пробелы, но вы можете отключить это либо глобально, либо по правилам, используя noskipws
(см. документы).
from textx import metamodel_from_str
def test_get_hosts():
grammar = r"""
config: ( /(?!host)./ | hosts+=host | 'host' )* ;
host: 'host' hostname=ID '{'
(
('hardware ethernet' hardware_ethernet=/[0-9a-fA-F:]+/';')?
'fixed-address' fixed_address=/([0-9]{1,3}\.){3}[0-9]{1,3}/';'
('option host-name' option_host_name=STRING';')?
('ddns-hostname' ddns_hostname=STRING';')?
)#
'}'
;
"""
conf_file = r"""
host example1 {
option host-name "example1";
ddns-hostname "example1";
fixed-address 192.168.1.181;
}
some arbitrary content in between
with word host but that fails to match host config.
host example2 {
hardware ethernet aa:bb:ff:20:fa:13;
fixed-address 192.168.1.191;
option host-name "example2";
ddns-hostname "example2";
}
"""
mm = metamodel_from_str(grammar)
model = mm.model_from_str(conf_file)
assert len(model.hosts) == 2
for host in model.hosts:
print(host.hostname, host.fixed_address)
if __name__ == "__main__":
test_get_hosts()
Изменить: вот еще две идеи для config
правило: простое:
config: ( hosts+=host | /./ )* ;
И (возможно) более производительный, который потребляет столько, сколько может, используя движок регулярных выражений, прежде чем пытаться host
:
config: ( /(?s:.*?(?=host))/ hosts*=host | 'host' )*
/(?s).*/;