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