Veo muchos ejemplos y páginas de manual sobre cómo hacer cosas como buscar y reemplazar usando sed, awk o gawk.
Pero en mi caso, tengo una expresión regular que quiero ejecutar en un archivo de texto para extraer un valor específico. No quiero buscar y reemplazar. Esto se llama desde bash. Usemos un ejemplo:
Ejemplo de expresión regular:
.*abc([0-9]+)xyz.*
Archivo de entrada de ejemplo:
a
b
c
abc12345xyz
a
b
c
Tan simple como suena, no puedo entender cómo llamar a sed / awk / gawk correctamente. Lo que esperaba hacer es que mi script bash tenga:
myvalue=$( sed <...something...> input.txt )
Las cosas que he probado incluyen:
sed -e 's/.*([0-9]).*/\\1/g' example.txt # extracts the entire input file
sed -n 's/.*([0-9]).*/\\1/g' example.txt # extracts nothing

Respuestas:
Mi
sed(Mac OS X) no funcionó con+. Intenté en su*lugar y agregué unapetiqueta para imprimir coincidencia:Para hacer coincidir al menos un carácter numérico sin
+, usaría:fuente
+y luego funcionó para mí:sed -n 's/^.*abc\([0-9]\+\)xyz.*$/\1/p'Puedes usar sed para hacer esto
-nno imprima la línea resultante-resto hace que no tenga el escape del grupo de captura parens().\1el partido del grupo de captura/gpartido global/pimprimir el resultadoEscribí una herramienta para mí que lo hace más fácil.
fuente
Utilizo
perlpara hacer esto más fácil para mí. p.ejEsto ejecuta Perl, la
-nopción le indica a Perl que lea en una línea a la vez desde STDIN y ejecute el código. La-eopción especifica la instrucción a ejecutar.La instrucción ejecuta una expresión regular en la línea leída y, si coincide, imprime el contenido del primer conjunto de corchetes (
$1).También puede hacer esto con varios nombres de archivo al final. p.ej
perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/' example1.txt example2.txtfuente
Si su versión de lo
grepadmite, puede usar la-oopción para imprimir solo la parte de cualquier línea que coincida con su expresión regular.Si no es así, aquí está lo mejor
sedque se me ocurrió:... que elimina / omite sin dígitos y, para las líneas restantes, elimina todos los caracteres iniciales y finales que no son dígitos. (Solo supongo que su intención es extraer el número de cada línea que contiene uno).
El problema con algo como:
.... o
... es que
sedsolo admite coincidencias "codiciosas" ... por lo que el primero. * coincidirá con el resto de la línea. A menos que podamos usar una clase de carácter negada para lograr una coincidencia no codiciosa ... o una versiónsedcompatible con Perl u otras extensiones para sus expresiones regulares, no podemos extraer una coincidencia de patrón precisa con el espacio de patrón (una línea ).fuente
sedcomandos de esta manera:sed -n 's/[^0-9]*\([0-9]\+\).*/\1/p'grep -o! Estaba tratando de hacer estosedy luché con mi necesidad de encontrar múltiples coincidencias en algunas líneas. Mi solución es stackoverflow.com/a/58308239/117471Puede utilizar
awkconmatch()para acceder al grupo capturado:Esto intenta coincidir con el patrón
abc[0-9]+xyz. Si lo hace, almacena sus cortes en la matrizmatches, cuyo primer elemento es el bloque[0-9]+. Dado quematch()devuelve la posición del carácter, o índice, de donde comienza esa subcadena (1, si comienza al principio de la cadena) , activa laprintacción.Con
greppuede utilizar una mirada hacia atrás y hacia adelante:Esto comprueba el patrón
[0-9]+cuando se produce dentroabcyxyzya sólo imprime los dígitos.fuente
perl es la sintaxis más limpia, pero si no tiene perl (no siempre está ahí, según tengo entendido), entonces la única forma de usar gawk y los componentes de una expresión regular es usar la función gensub.
la salida del archivo de entrada de muestra será
Nota: gensub reemplaza toda la expresión regular (entre //), por lo que debe colocar. * Antes y después de ([0-9] +) para eliminar el texto antes y después del número en la sustitución.
fuente
match()para acceder a los grupos capturados. Vea mi respuesta para esto.Si desea seleccionar líneas, elimine los bits que no desea:
Básicamente, selecciona las líneas que desea
egrepy luego las usasedpara quitar los bits antes y después del número.Puedes ver esto en acción aquí:
Actualización: obviamente, si su situación real es más compleja, los RE necesitarán modificarlos. Por ejemplo, si siempre tuvo un solo número enterrado dentro de cero o más números no numéricos al principio y al final:
fuente
El caso del OP no especifica que puede haber múltiples coincidencias en una sola línea, pero para el tráfico de Google, también agregaré un ejemplo para eso.
Dado que la necesidad del OP es extraer un grupo de un patrón, el uso
grep -orequerirá 2 pasadas. Pero, todavía encuentro que esta es la forma más intuitiva de hacer el trabajo.Dado que el tiempo del procesador es básicamente gratuito, pero la legibilidad humana no tiene precio, tiendo a refactorizar mi código en función de la pregunta, "dentro de un año, ¿qué voy a pensar que hace esto?" De hecho, para el código que tengo la intención de compartir públicamente o con mi equipo, incluso abriré
man greppara averiguar cuáles son las opciones largas y sustituirlas. Al igual que:grep --only-matching --extended-regexpfuente
puedes hacerlo con el caparazón
fuente
Por awk. Usaría el siguiente script:
fuente
([0-9+])genera el valor numérico , esto genera la línea completa.fuente