Tengo un archivo que tiene muchas líneas aleatorias como
aaa bbb
ccc ddd
eee mark: 98 fff
ggg ggg jjjj iii
jjj kkkk
Quiero usar awk Y solo gensub para que coincida con el número "98" anterior. Hasta ahora tengo este código a continuación, creo que no funciona porque necesito hacer que gensub trate "\n" como cualquier otro carácter.
cat file.txt | awk 'printf(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}'
Necesito que el resultado del código anterior sea solo "98". ¿Cómo puedo hacer eso?
EDITAR
incluso cuando uso el modificador s o m no funciona como debería porque, hasta donde yo sé, el modificador "s" debería hacer que la expresión regular sea tratar. como cualquier carácter incluido \n.
Respuesta1
Parece pensar que awk
trata su entrada como una cadena de varias líneas. No es así. Cuando ejecuta un script awk en un archivo, el script se aplicaa cada línea del archivopor separado. Entonces, se gensub
ejecutó una vez por línea. De hecho, puedes hacer lo que quieras, awk
pero realmente no es la mejor herramienta para el trabajo.
Por lo que puedo decir, tienes un archivo grande y solo quieres imprimir un número que viene después mark:
y un espacio en blanco. Si es así, todos estos enfoques son más simples que jugar con gensub
:
Usar
grep
con expresiones regulares compatibles con Perl (-P
)$ grep -oP 'mark:\s*\K\d+' file 98
Las
-o
marcasgrep
solo imprimen la parte coincidente de la línea. Es\K
una construcción PCRE que significa "ignorar todo lo que coincida antes de este punto".sed
$ sed -n 's/.*mark:\s*\([0-9]\+\).*/\1/p' file 98
Esto
-n
suprime la salida normal. Alp
final sesed
imprime solo si la sustitución fue exitosa. La expresión regular captura una cadena de números a continuaciónmark:
y 0 o más espacios en blanco y reemplaza toda la línea con lo que se capturó.perla
$ perl -ne 'print if s/.*mark:\s*(\d+).*/$1/' file 98
Le
-n
dice a Perl que lea un archivo de entrada línea por línea y aplique el script proporcionado por-e
. El script imprimirá cualquier línea donde la sustitución haya sido exitosa.
Si realmente quieres usar gensub
, puedes hacer algo como:
$ awk '/mark:/{print gensub(/.*mark:\s*([0-9]+).*/,"\\1","g")}' file
98
Personalmente, lo haría de esta manera en awk:
$ awk '/mark:/{gsub(/[^0-9]/,"");print}' file
98
Dado que parecía estar intentando que awk recibiera entradas multilínea, así es como puede hacerlo (suponiendo que no haya caracteres NULL en su archivo):
$ awk '{print(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}' RS='\0' file
98
Establece RS='\0'
el separador de registros de entrada (que es lo que define una "línea" para awk
) en \0
. Dado que no existen tales caracteres en su archivo, esto resulta en awk
leerlo todo de una vez.
Respuesta2
El cambio más pequeño para que funcione será:
cat file | awk '/mark:/{printf( "%s\n",gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}'
/mark:/ es para seleccionar una línea que contenga "mark:".
Pero entonces, ¿por qué se necesita un printf? Esto también funcionará:
cat file | awk '/mark:/{print(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}'
Pero eso sería un "uso inútil del gato", como awk podría leer directamente desde un archivo:
awk '/mark:/{print(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}' file
Editar:
A petición del usuario: cómo utilizar la expresión regular en archivos y cadenas.
Bueno, con las reglas que estableciste: awk solo con gensub no es posible.
Además, la idea de hacer coincidir con .*mark: ([0-9]+).*
para reemplazar todo eso con la coincidencia dentro del paréntesis significará que es necesario hacer coincidir todo el archivo para extraer una parte. Esa es una de las razones por las que se creó grep.
Solo usa:
grep -oP "mark: \K([0-9]+)" file
o:
echo "$string" | grep -oP "mark: \K([0-9]+)"
Y obtendrás el resultado.