RegExp: elimina el último período de la cadena que puede contener otros períodos (salida de excavación)

Estoy tratando de analizar la salida del comando linux dig y hacer varias cosas de una sola vez con expresiones regulares.

Digamos que cavo el host mail.yahoo.com :

 /usr/bin/dig +nocomments +noquestion \ +noauthority +noadditional +nostats +nocmd \ mail.yahoo.com A 

Este comando genera:

 mail.yahoo.com. 0 IN CNAME login.yahoo.com. login.yahoo.com. 0 IN CNAME ats.login.lgg1.b.yahoo.com. ats.login.lgg1.b.yahoo.com. 0 IN CNAME ats.member.g02.yahoodns.net. ats.member.g02.yahoodns.net. 0 IN CNAME any-ats.member.a02.yahoodns.net. any-ats.member.a02.yahoodns.net. 12 IN A 98.139.21.169 

Lo que me gustaría es encontrar todas las partes , y sin el período final usando solo una expresión regular

Para este ejemplo en particular con mail.yahoo.com , sería:

 [ ('mail.yahoo.com', 'CNAME', 'login.yahoo.com'), ('login.yahoo.com', 'CNAME', 'ats.login.lgg1.b.yahoo.com'), ('ats.login.lgg1.b.yahoo.com', 'CNAME', 'ats.member.g02.yahoodns.net'), ('ats.member.g02.yahoodns.net', 'CNAME', 'any-ats.member.a02.yahoodns.net'), ('any-ats.member.a02.yahoodns.net', 'A', '98.139.21.169'), ] 

Pero resulta que el comando de dig podría estar mostrando un punto al final del nombre:

  mail.yahoo.com. ^ ^ ^ | | | Good dot | | | | Good dot | | (!) Baaaad dot 

Hacer una expresión regular que divida la salida de dig y devuelva el nombre con el período final es bastante sencillo:

 regex = re.compile("^(\S+).+IN\s+([AZ]+)\s+(\S+)\.*\s*$",re.MULTILINE) 

Pero llamar a .findall con esa expresión regular devuelve el período final en el host, porque \S+ también coincidirá con el último período:

 [ ('mail.yahoo.com.', 'CNAME', 'login.yahoo.com.'), ('login.yahoo.com.', 'CNAME', 'ats.login.lgg1.b.yahoo.com.'), ('ats.login.lgg1.b.yahoo.com.', 'CNAME', 'ats.member.g02.yahoodns.net.'), ('ats.member.g02.yahoodns.net.', 'CNAME', 'any-ats.member.a02.yahoodns.net.'), ('any-ats.member.a02.yahoodns.net.', 'A', '98.139.21.169'), ] 

Así que necesitaría algo que coincida con todos los no espacios \S excepto si es un período seguido de un espacio en blanco.

He hecho un sinfín de bashs y no he podido encontrar una solución decente.

¡Gracias de antemano!

PD: Sé que siempre puedo usar la expresión regular “fácil” y (en una segunda pasada) eliminar el último punto de la cadena encontrada, pero tengo curiosidad acerca de si esto se puede hacer con una expresión regular en una toma.

Puedes usar este patrón con modificador multilínea:

 ^([^ ]+)(? 

Grupos almacenados en $ 1 $ 2 y $ 3

MANIFESTACIÓN

Editar: Prueba esto:

 ^([^ \t]+)(? 

Pero llamar a .findall con esa expresión regular devuelve el período final en el host, porque \S+ también coincidirá con el último período …

Hay dos problemas aquí.

Primero, una vez que estás escapando de las cosas con barras invertidas, necesitas usar literales de cadena en bruto ( r"…" ), o también debes escapar de las barras invertidas. No estoy realmente seguro de si alguno de sus caracteres con prefijo de barra invertida coincide con las secuencias de escape de barra invertida de Python, pero eso en sí mismo es razón suficiente para usar un literal de cadena cruda, por lo que sus lectores no tienen que buscar exactamente reglas.

En segundo lugar, el caso general de este problema es que las repeticiones de expresiones regulares son codiciosas de forma predeterminada: coincidirán tanto como sea posible y al mismo tiempo permitirán que el rest del patrón coincida; cuando desea que coincidan lo menos posible y al mismo tiempo permite que el rest del patrón coincida, ¿necesita agregar un ? después de + o * .

En su caso particular, el \S+ puede hacer coincidir todo hasta la final . y el \.*\s* coincidirá exitosamente con 0 . s y 0 espacios. pero \S+? saldrá la final . para la siguiente parte del patrón. También puede forzar el período fuera del primer grupo agregando un período posterior. Al igual que:

 ^(\S+)\..+IN\s+([AZ]+)\s+(\S+?)\.*\s*$ 

Visualización de expresiones regulares

Demo Debuggex

Simplemente puede forzar que no haya un período al final de su grupo (y que no contenga espacio):

 npg = '([^\.\s]+(?:.[^\.\s]+)*)' #not_period_ending_group regex = re.compile("^" + npg + ".+IN\s+([AZ]+)\s+" + npg +".+$",re.MULTILINE) 

Como una respuesta alternativa, sugiero usar str.split() , si tiene sus líneas de cadena en una lista como L , necesita esto:

 [(line[0][:-1],line[3],line[4][:-1]) for line in L] 

Tenga en cuenta que [:-1] elimina el último . desde la dirección del host!