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 awk
trata 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ê gensub
foi executado uma vez por linha. Na verdade, você pode fazer o que quiser, awk
mas 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
:
Usar
grep
com expressões regulares compatíveis com Perl (-P
)$ grep -oP 'mark:\s*\K\d+' file 98
As
-o
marcasgrep
imprimem apenas a parte correspondente da linha. É\K
uma construção PCRE que significa "ignorar qualquer coisa que corresponda antes deste ponto".sed
$ sed -n 's/.*mark:\s*\([0-9]\+\).*/\1/p' file 98
O
-n
suprime a saída normal. Op
finalsed
imprime apenas se a substituição for bem-sucedida. A própria regex captura uma sequência de números a seguirmark:
e 0 ou mais caracteres de espaço em branco e substitui a linha inteira pelo que foi capturado.Perl
$ perl -ne 'print if s/.*mark:\s*(\d+).*/$1/' file 98
Diz ao
-n
Perl 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 awk
leitura 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.