gensub em várias linhas

2

Eu tenho um arquivo que tem muitas linhas aleatórias, como

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

Eu quero usar o awk AND apenas o gensub para igualar o número "98" acima. Até agora eu tenho este código abaixo, eu acho que não funciona porque eu preciso fazer gensub tratar "\ n" como qualquer outro personagem.

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

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

EDITAR

mesmo quando eu uso o modificador s ou m, ele não funciona como deveria, tanto quanto eu sei que o modificador "s" deve fazer o regex treat. como qualquer caractere, incluindo \ n.

    
por Samul 01.11.2015 / 19:40

2 respostas

3

Você parece pensar que awk trata sua entrada como uma cadeia de múltiplas linhas. Não faz. Quando você executa um script awk em um arquivo, o script é aplicado para cada linha do arquivo separadamente. Então, seu gensub foi executado uma vez por linha. Você pode realmente fazer o que quiser com awk , mas realmente não é a melhor ferramenta para o trabalho.

Tanto quanto eu posso dizer, você tem um arquivo grande e só quer imprimir um número que vem depois de mark: e espaço em branco. Se sim, todas essas abordagens são mais simples do que brincar com gensub :

  1. Use grep com expressões regulares compatíveis com Perl ( -P )

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

    O -o faz com que grep imprima apenas a parte correspondente da linha. O \K é uma construção PCRE que significa "ignorar qualquer coisa correspondida antes desse ponto".

  2. sed

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

    O -n suprime a saída normal. O p no final torna sed print somente se a substituição foi bem-sucedida. O próprio regex captura uma sequência de números seguindo mark: e 0 ou mais caracteres de espaço em branco e substitui toda a linha pelo que foi capturado.

  3. Perl

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

    O -n diz ao perl para ler um arquivo de entrada linha por linha e aplicar o script fornecido por -e . O script imprimirá todas as linhas em que a substituição foi bem-sucedida.

Se você realmente quiser usar gensub , poderá 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

Desde que você parecia estar tentando obter o awk para receber entrada de múltiplas linhas, é assim que você pode fazer isso (assumindo que não haja caracteres NULL em seu arquivo):

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

O RS='awk' define o separador de registro de entrada (que é o que define uma "linha" para awk ) para %code% . Como não há tais caracteres em seu arquivo, isso resulta em %code% lendo tudo de uma vez.

    
por 01.11.2015 / 23:24
2

A menor alteração para fazê-lo funcionar será:

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

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

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

Mas isso seria um " uso inútil de cat ", como o awk poderia ler diretamente de um arquivo:

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

Editar:

Por solicitação do usuário: como usar o regex no arquivo e na string.

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

Use apenas:

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

ou:

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

E você obterá o resultado.

    
por 01.11.2015 / 20:06