Tengo un archivo que se parece a esto:
<table name="content_analyzer" primary-key="id">
<type="global" />
</table>
<table name="content_analyzer2" primary-key="id">
<type="global" />
</table>
<table name="content_analyzer_items" primary-key="id">
<type="global" />
</table>
Necesito extraer cualquier cosa entre las comillas que siguen name=
, es decir content_analyzer
, content_analyzer2
y content_analyzer_items
.
Estoy haciendo esto en una caja de Linux, por lo que una solución que use sed, perl, grep o bash está bien.
regex
perl
sed
html-parsing
text-extraction
vaquero
fuente
fuente
Respuestas:
Dado que necesita hacer coincidir el contenido sin incluirlo en el resultado (debe coincidir,
name="
pero no es parte del resultado deseado) se requiere alguna forma de coincidencia de ancho cero o captura de grupo. Esto se puede hacer fácilmente con las siguientes herramientas:Perl
Con Perl, puede usar la
n
opción para recorrer línea por línea e imprimir el contenido de un grupo de captura si coincide:perl -ne 'print "$1\n" if /name="(.*?)"/' filename
GNU grep
Si tiene una versión mejorada de grep, como GNU grep, puede tener la
-P
opción disponible. Esta opción habilitará una expresión regular similar a Perl, lo que le permitirá usar\K
una búsqueda retrospectiva abreviada. Restablecerá la posición de coincidencia, por lo que cualquier cosa antes de que sea de ancho cero.grep -Po 'name="\K.*?(?=")' filename
La
o
opción hace que grep imprima solo el texto coincidente, en lugar de la línea completa.Vim - Editor de texto
Otra forma es utilizar un editor de texto directamente. Con Vim, una de las diversas formas de lograr esto sería eliminar las líneas sin
name=
y luego extraer el contenido de las líneas resultantes::v/.*name="\v([^"]+).*/d|%s//\1
Grep estándar
Si no tiene acceso a estas herramientas, por alguna razón, se podría lograr algo similar con grep estándar. Sin embargo, sin mirar alrededor, será necesario limpiarlo más tarde:
grep -o 'name="[^"]*"' filename
Una nota sobre cómo guardar resultados
En todos los comandos anteriores, se enviarán los resultados a
stdout
. Es importante recordar que siempre puede guardarlos conectándolos a un archivo agregando:hasta el final del comando.
fuente
grep
):grep -Po '.*name="\K.*?(?=".*)'
.*
un lado, espero que no se enoje conmigo. Me gustaría preguntar, ¿ve algún beneficio de un partido sin codicia sobre "cualquier cosa excepto"
"? No tome esto como una pelea, solo tengo curiosidad y no soy un experto en expresiones regulares. Además, la\K
propina, muy bonita. Gracias Dennis..*
, puedes hacerlogrep -Po '(?<=name=").*?(?=")'
. Se\K
puede usar para taquigrafía, pero en realidad solo es necesario si la coincidencia a su izquierda es de longitud variable. En casos como este, la razón para usar alternativas es bastante obvia. Las operaciones poco codiciadas se ven un poco más ordenadas ([^"]*
versus.*?
y no tienes que repetir el personaje principal. No sé sobre la velocidad. Eso depende mucho del contexto, creo. Espero que sea útil.\K
(después de investigarlo) y eliminé el.*
fue el mismo: hacer que se vea bonito (más simple). Y nunca pensé en usar en.*?
lugar de la "forma tradicional" que aprendí de alguna parte. Pero la falta de codicia aquí realmente tiene sentido. Gracias Dennis, mis mejores deseos.La expresión regular sería:
.+name="([^"]+)"
Entonces la agrupación estaría en el \ 1
fuente
Si está utilizando Perl, descargue un módulo para analizar XML: XML :: Simple , XML :: Twig o XML :: LibXML . No reinventes la rueda.
fuente
<type="global"
por ejemplo), por lo que la mayoría de los analizadores XML simplemente se quejan y mueren.Se debe utilizar un analizador HTML para este propósito en lugar de expresiones regulares. Un programa de Perl que utiliza
HTML::TreeBuilder
:Programa
#!/usr/bin/env perl use strict; use warnings; use HTML::TreeBuilder; my $tree = HTML::TreeBuilder->new_from_file( \*DATA ); my @elements = $tree->look_down( sub { defined $_[0]->attr('name') } ); for (@elements) { print $_->attr('name'), "\n"; } __DATA__ <table name="content_analyzer" primary-key="id"> <type="global" /> </table> <table name="content_analyzer2" primary-key="id"> <type="global" /> </table> <table name="content_analyzer_items" primary-key="id"> <type="global" /> </table>
Salida
fuente
esto podría hacerlo:
perl -ne 'if(m/name="(.*?)"/){ print $1 . "\n"; }'
fuente
Aquí hay una solución que utiliza HTML tidy y xmlstarlet:
htmlstr=' <table name="content_analyzer" primary-key="id"> <type="global" /> </table> <table name="content_analyzer2" primary-key="id"> <type="global" /> </table> <table name="content_analyzer_items" primary-key="id"> <type="global" /> </table> ' echo "$htmlstr" | tidy -q -c -wrap 0 -numeric -asxml -utf8 --merge-divs yes --merge-spans yes 2>/dev/null | sed '/type="global"/d' | xmlstarlet sel -N x="http://www.w3.org/1999/xhtml" -T -t -m "//x:table" -v '@name' -n
fuente
Vaya, el comando sed debe preceder al comando tidy, por supuesto:
echo "$htmlstr" | sed '/type="global"/d' | tidy -q -c -wrap 0 -numeric -asxml -utf8 --merge-divs yes --merge-spans yes 2>/dev/null | xmlstarlet sel -N x="http://www.w3.org/1999/xhtml" -T -t -m "//x:table" -v '@name' -n
fuente
Si la estructura de su xml (o texto en general) es fija, la forma más fácil es usar
cut
. Para su caso específico:echo '<table name="content_analyzer" primary-key="id"> <type="global" /> </table> <table name="content_analyzer2" primary-key="id"> <type="global" /> </table> <table name="content_analyzer_items" primary-key="id"> <type="global" /> </table>' | grep name= | cut -f2 -d '"'
fuente