gensub en múltiples líneas

gensub en múltiples líneas

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 awktrata 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 gensubejecutó una vez por línea. De hecho, puedes hacer lo que quieras, awkpero 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:

  1. Usar grepcon expresiones regulares compatibles con Perl ( -P)

    $ grep -oP 'mark:\s*\K\d+' file 
    98
    

    Las -omarcas grepsolo imprimen la parte coincidente de la línea. Es \Kuna construcción PCRE que significa "ignorar todo lo que coincida antes de este punto".

  2. sed

    $ sed -n 's/.*mark:\s*\([0-9]\+\).*/\1/p' file
    98
    

    Esto -nsuprime la salida normal. Al pfinal se sedimprime solo si la sustitución fue exitosa. La expresión regular captura una cadena de números a continuación mark:y 0 o más espacios en blanco y reemplaza toda la línea con lo que se capturó.

  3. perla

    $ perl -ne 'print if s/.*mark:\s*(\d+).*/$1/' file
    98
    

    Le -ndice 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 awkleerlo 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.

información relacionada