¿cómo usar sed, awk o gawk para imprimir solo lo que coincide?
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 contra un archivo de texto para extraer un valor específico. No quiero buscar y reemplazar. Esto está siendo llamado 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 esto, no puedo averiguar cómo llamar sed / awk / gawk correctamente. Lo que esperaba hacer, es desde dentro de mi guión bash tienen:
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
10 answers
Mi sed
(Mac OS X) no funcionaba con +
. Probé *
en su lugar y agregué p
etiqueta para imprimir 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-14 08:50:20
Puedes usar sed para hacer esto
sed -rn 's/.*abc([0-9]+)xyz.*/\1/gp'
-
-n
no imprima la línea resultante -
-r
esto hace que no tengas el escape del grupo de captura parens()
. -
\1
el grupo de captura coincide -
/g
partido global -
/p
imprimir el resultado
Escribí una herramienta para mí que hace esto más fácil
rip 'abc(\d+)xyz' '$1'
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-03 19:44:36
Uso perl
para hacer esto más fácil para mí. por ejemplo,
perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/'
Esto ejecuta Perl, la opción -n
le indica a Perl que lea una línea a la vez desde STDIN y ejecute el código. La opción -e
especifica la instrucción a ejecutar.
La instrucción ejecuta una regexp en la línea leída, y si coincide imprime el contenido del primer conjunto de bracks ($1
).
También puede hacer esto con varios nombres de archivo al final. por ejemplo,
perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/' example1.txt example2.txt
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-28 13:51:47
Si su versión de grep
lo soporta, podría usar la opción -o
para imprimir solo la porción de cualquier línea que coincida con su expresión regular.
Si no, entonces aquí está lo mejor sed
que podría llegar a:
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 iniciales y finales que no son dígitos. (Solo estoy adivinando que su 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 la coincidencia "codiciosa"... así que la primera .* coincidirá con el resto de la línea. A menos que podamos usar una clase de personaje negada para lograr una coincidencia no codiciosa ... o una versión de sed
con compatible con Perl u otras extensiones a sus expresiones regulares, no podemos extraer una coincidencia precisa de patrón con el espacio de patrón (una línea).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-14 10:56:46
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 coincidir con el patrón abc[0-9]+xyz
. Si lo hace, almacena sus slices en el array matches
, cuyo primer elemento es el bloque [0-9]+
. Desde 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 print
.
Con grep
puede usar una mirada atrás y mirar hacia adelante:
$ grep -oP '(?<=abc)[0-9]+(?=xyz)' file
12345
$ grep -oP 'abc\K[0-9]+(?=xyz)' file
12345
Esto comprueba el patrón [0-9]+
cuando ocurre dentro de abc
y xyz
y solo imprime los dígitos.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-22 09:01:11
Perl es la sintaxis más limpia, pero si no tiene perl (no siempre está ahí, entiendo), entonces la única manera de usar gawk y componentes de una expresión regular es usar la característica gensub.
gawk '/abc[0-9]+xyz/ { print gensub(/.*([0-9]+).*/,"\\1","g"); }' < file
La salida del archivo de entrada de muestra será
12345
Nota: gensub reemplaza toda la expresión regular (entre el //), por lo que debe poner el .* antes y después del ([0-9]+) para deshacerse del texto antes y después del número en la sustitución.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-22 22:19:55
Si desea seleccionar líneas, elimine los bits que no desea:
egrep 'abc[0-9]+xyz' inputFile | sed -e 's/^.*abc//' -e 's/xyz.*$//'
Básicamente selecciona las líneas que desea 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 la situación real es más compleja, el REs tendrá que mí modificado. Por ejemplo, si siempre tenía un solo número enterrado dentro de cero o más no numéricos al principio y fin:
egrep '[^0-9]*[0-9]+[^0-9]*$' inputFile | sed -e 's/^[^0-9]*//' -e 's/[^0-9]*$//'
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-14 08:59:30
Puedes hacerlo con el shell
while read -r line
do
case "$line" in
*abc*[0-9]*xyz* )
t="${line##abc}"
echo "num is ${t%%xyz}";;
esac
done <"file"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-28 01:58:22
Para awk. Usaría el siguiente script:
/.*abc([0-9]+)xyz.*/ {
print $0;
next;
}
{
/* default, do nothing */
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-14 08:54:58
gawk '/.*abc([0-9]+)xyz.*/' file
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-14 09:18:02