reemplazar regex unix sed awk gawk

regex - reemplazar - sed linux



cómo usar sed, awk o gawk para imprimir solo lo que coincide? (10)

Veo muchos ejemplos y páginas man 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 contra 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.*

Ejemplo de archivo de entrada:

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 desde dentro de mi script bash tener:

myvalue=$( sed <...something...> input.txt )

Las cosas que he intentado 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


Mi sed (Mac OS X) no funcionaba con + . Intenté * lugar y agregué la etiqueta p para imprimir la coincidencia:

sed -n ''s/^.*abc/([0-9]*/)xyz.*$//1/p'' example.txt

Para hacer coincidir al menos un carácter numérico sin + , usaría:

sed -n ''s/^.*abc/([0-9][0-9]*/)xyz.*$//1/p'' example.txt


Por awk. Yo usaría la siguiente secuencia de comandos:

/.*abc([0-9]+)xyz.*/ { print $0; next; } { /* default, do nothing */ }


Puede usar awk con match() para acceder al grupo capturado:

$ awk ''match($0, /abc([0-9]+)xyz/, matches) {print matches[1]}'' file 12345

Esto intenta hacer coincidir el patrón abc[0-9]+xyz . Si lo hace, almacena sus divisiones en la matriz matches , cuyo primer elemento es el bloque [0-9]+ . Como match() devuelve la posición del carácter, o índice, de donde comienza esa subcadena (1, si comienza al principio de la cadena) , desencadena la acción de print .

Con grep puedes usar look-behind y look-ahead:

$ grep -oP ''(?<=abc)[0-9]+(?=xyz)'' file 12345 $ grep -oP ''abc/K[0-9]+(?=xyz)'' file 12345

Esto verifica el patrón [0-9]+ cuando ocurre dentro de abc y xyz y solo imprime los dígitos.


Puedes usar sed para hacer esto

sed -rn ''s/.*abc([0-9]+)xyz.*//1/gp''

  • -n no imprime la línea resultante
  • -r esto lo hace para que no tenga el escape del grupo de captura parens () .
  • /1 el grupo de captura coincide
  • /g partido global
  • /p imprime el resultado

Escribí una tool para mí que hace esto más fácil

rip ''abc(/d+)xyz'' ''$1''


Si desea seleccionar líneas, elimine las que no desea:

egrep ''abc[0-9]+xyz'' inputFile | sed -e ''s/^.*abc//'' -e ''s/xyz.*$//''

Básicamente selecciona las líneas que deseas con egrep y luego usa sed para quitar los bits antes y después del número.

Puedes ver esto en acción aquí:

pax> echo ''a b c abc12345xyz a b c'' | egrep ''abc[0-9]+xyz'' | sed -e ''s/^.*abc//'' -e ''s/xyz.*$//'' 12345 pax>

Actualización: obviamente, si tu situación real es más compleja, las RE necesitarán modificarme. Por ejemplo, si siempre tuvo un número único enterrado dentro de cero o más elementos no numéricos al inicio y al final:

egrep ''[^0-9]*[0-9]+[^0-9]*$'' inputFile | sed -e ''s/^[^0-9]*//'' -e ''s/[^0-9]*$//''


Si su versión de grep admite, podría usar la opción -o para imprimir solo la parte de cualquier línea que coincida con su expresión regular.

Si no, aquí está el mejor sed que pude encontrar:

sed -e ''/[0-9]/!d'' -e ''s/^[^0-9]*//'' -e ''s/[^0-9]*$//''

... que elimina / salta sin dígitos y, para las líneas restantes, elimina todos los caracteres que no son de dígito inicial y final. (Solo estoy adivinando que tu intención es extraer el número de cada línea que contiene uno).

El problema con algo como:

sed -e ''s/.*/([0-9]*/).*/&/''

.... o

sed -e ''s/.*/([0-9]*/).*//1/''

... es que sed solo admite compatibilidad "codiciosa" ... por lo que la primera. * coincidirá con el resto de la línea. A menos que podamos usar una clase de caracteres negada para lograr una coincidencia no codiciosa ... o una versión de sed con Perl-compatible u otras extensiones a sus expresiones regulares, no podemos extraer una coincidencia exacta de patrones con el espacio de patrones (una línea).


Yo uso perl para hacer esto más fácil para mí. p.ej

perl -ne ''print $1 if /.*abc([0-9]+)xyz.*/''

Esto ejecuta Perl, la opción -n indica a Perl que lea en una línea a la vez desde STDIN y ejecute el código. La opción -e especifica las instrucciones para ejecutar.

La instrucción ejecuta una expresión regular en la línea leída, y si coincide imprime los contenidos del primer conjunto de bloqueos ( $1 ).

Usted puede hacer esto también múltiples nombres de archivo al final. p.ej

perl -ne ''print $1 if /.*abc([0-9]+)xyz.*/'' example1.txt example2.txt


perl es la sintaxis más limpia, pero si no tienes perl (no siempre está ahí, entiendo), entonces la única forma de usar gawk y componentes de una expresión regular es usar la función gensub.

gawk ''/abc[0-9]+xyz/ { print gensub(/.*([0-9]+).*/,"//1","g"); }'' < file

salida del archivo de entrada de muestra será

12345

Nota: gensub reemplaza toda la expresión regular (entre //), por lo que debe colocar el. * Antes y después de ([0-9] +) para eliminar el texto antes y después del número en la sustitución.


puedes hacerlo con el caparazón

while read -r line do case "$line" in *abc*[0-9]*xyz* ) t="${line##abc}" echo "num is ${t%%xyz}";; esac done <"file"


gawk ''/.*abc([0-9]+)xyz.*/'' file