gensub em múltiplas linhas

gensub em múltiplas linhas

Eu tenho um arquivo que contém muitas linhas aleatórias como

aaa bbb
ccc ddd
eee mark: 98 fff
ggg ggg jjjj iii
jjj kkkk

Quero usar awk E apenas gensub para corresponder ao número "98" acima. Até o momento tenho esse código abaixo, acho que não funciona pois preciso fazer o gensub tratar "\n" como qualquer outro caractere.

cat file.txt | awk 'printf(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}'

Preciso que a saída do código acima seja apenas "98". Como faço isso?

EDITAR

mesmo quando uso o modificador s ou m ele não funciona como deveria causar até onde eu sei o modificador "s" deveria fazer regex treat . como qualquer caractere, incluindo \n.

Responder1

Você parece pensar que awktrata sua entrada como uma string multilinha. Isso não acontece. Quando você executa um script awk em um arquivo, o script é aplicadopara cada linha do arquivoseparadamente. Então, você gensubfoi executado uma vez por linha. Na verdade, você pode fazer o que quiser, awkmas realmente não é a melhor ferramenta para o trabalho.

Pelo que sei, você tem um arquivo grande e deseja imprimir apenas um número que vem depois mark:e um espaço em branco. Nesse caso, todas essas abordagens são mais simples do que brincar com gensub:

  1. Usar grepcom expressões regulares compatíveis com Perl ( -P)

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

    As -omarcas grepimprimem apenas a parte correspondente da linha. É \Kuma construção PCRE que significa "ignorar qualquer coisa que corresponda antes deste ponto".

  2. sed

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

    O -nsuprime a saída normal. O pfinal sedimprime apenas se a substituição for bem-sucedida. A própria regex captura uma sequência de números a seguir mark:e 0 ou mais caracteres de espaço em branco e substitui a linha inteira pelo que foi capturado.

  3. Perl

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

    Diz ao -nPerl para ler um arquivo de entrada linha por linha e aplicar o script fornecido por -e. O script imprimirá todas as linhas onde a substituição foi bem-sucedida.

Se você realmente quiser usar gensub, você pode fazer algo como:

$ awk '/mark:/{print gensub(/.*mark:\s*([0-9]+).*/,"\\1","g")}' file
98

Pessoalmente, eu faria assim no awk:

$ awk '/mark:/{gsub(/[^0-9]/,"");print}' file
98

Como você parecia estar tentando fazer com que o awk recebesse entrada multilinha, é assim que você pode fazer isso (supondo que não haja caracteres NULL em seu arquivo):

$ awk '{print(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}' RS='\0' file
98

Define RS='\0'o separador de registro de entrada (é o que define uma "linha" para awk) como \0. Como não existem tais caracteres em seu arquivo, isso resulta na awkleitura de tudo de uma vez.

Responder2

A menor mudança para fazê-lo funcionar será:

cat file | awk '/mark:/{printf( "%s\n",gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}'

O /mark:/ serve para selecionar uma linha que contém "mark:".
Mas, então, por que é necessário um printf? Isso também funcionará:

cat file | awk '/mark:/{print(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}'

Mas isso seria um "uso inútil de gato", já que o awk poderia ler diretamente de um arquivo:

awk '/mark:/{print(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}' file

Editar:

A pedido do usuário: Como usar o regex em arquivo e string.

Bem, com as regras que você definiu: awk apenas com gensub não é possível.
Além disso, a ideia de combinar com .*mark: ([0-9]+).*para substituir tudo isso pela correspondência entre parênteses significará que é necessário combinar o arquivo inteiro para extrair uma parte. Essa é uma das razões pelas quais o grep foi criado.

Apenas use:

grep -oP "mark: \K([0-9]+)" file

ou:

echo "$string" | grep -oP "mark: \K([0-9]+)"

E você obterá o resultado.

informação relacionada