Imprime linhas onde o primeiro campo tem apenas quatro caracteres usando regex no awk?

3
John Goldenrod:(916) 348-4278:250:100:175

Chet Main:(510) 548-5258:50:95:135

Tom Savage:(408) 926-3456:250:168:200

Elizabeth Stachelin:(916) 440-1763:175:75:300
A saída

deve conter as linhas contendo nomes com apenas quatro caracteres (joão, chet):

awk '$1 ~ /[a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9]" "/ {print}' file

isso não parece funcionar para mim. posso fazê-lo sem usar nenhuma das funções do awk?

    
por munish 24.11.2012 / 07:31

3 respostas

6

Os campos no awk são por padrão delimitados por " ", isso significa que $1 não contém um espaço, portanto, o regex correto para $1 é:

awk '$1 ~ /^[a-zA-Z0-9]{4}$/ {print}' file

Se você quiser manter sua abordagem original, também pode usar apenas $0 , por exemplo:

awk '$0 ~ /^[a-zA-Z0-9]{4}\s/ {print}' file

Para simplificar, você também pode usar \w em vez de definir explicitamente caracteres de palavras, por exemplo:

awk '$0 ~ /^\w{4}\s/ {print}' file

Se você quiser apenas corresponder ao espaço e não algo como TAB , basta substituir \s por " " (sem as aspas).

Outro problema com sua abordagem original são as âncoras ausentes. Como você não especificou ^ nem $ , seu padrão pode ocorrer em qualquer lugar, ou seja, o padrão corresponderia a Elizabeth Stachelin com beth .

    
por 24.11.2012 / 07:41
3

No AWK, você pode usar a expressão regular como um padrão como BEGIN ou END que você vê frequentemente no script AWK. Um código simplificado pode ser como

awk '/^[[:alnum:]]{4}\>/'

Isso é tudo que você precisa para atender às suas necessidades. Você não precisa de uma ação , {print} é a ação padrão quando um patten é correspondido, o que imprime o registro inteiro, ou seja, a linha inteira.

[:alnum:] é um sinônimo para [a-zA-Z0-9] basicamente, dependendo da localidade. Você também pode usar \w - apenas inclui sublinhado _ , é uma abreviação de [[:alnum:]_] :

awk '/^\w{4}\>/'

\> corresponde ao final de uma palavra. Ao usá-lo, você pode combinar a string como John:(###)... corretamente, se você tiver registros que não contenham os nomes completos.

Embora você esteja perguntando ao AWK, mas eu sugiro usar sed , ele é executado quase duas vezes mais rápido que o AWK no caso:

sed -n '/^[[:alnum:]]\{4\}\b/p'

\b é \> ou \< no AWK. Eu testei em linhas de 500K, 100K linhas correspondidas, AWK levou cerca de 1,7 segundos, sed levou apenas 0,9 segundos. Mas o caso de teste é extremo, é apenas uma pequena sugestão.

Também sugiro que você leia man 7 regex , bem como man awk e info awk .

    
por 24.11.2012 / 11:35
1

O primeiro campo é $1 e seu comprimento é length($1) , então:

awk 'length($1) == 4 {print}'

ou mais sucintamente

awk 'length($1) == 4'

O que você escreveu não funciona por dois motivos. Primeiro, você tem um " " extra no seu regexp, então você está exigindo que os campos contenham aspas duplas, espaço, aspas duplas. Se você corrigir isso, receberá /[a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9]/ , que corresponde a um campo que contém pelo menos quatro letras ou dígitos ASCII, mas pode conter mais, portanto, corresponderá a Elizabeth , bem como John , mas não Tom . Você pode escrever /^[a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9]$/ para ancorar o regexp no início e no fim, mas se o que você está procurando for o tamanho do campo, apenas escreva isso.

    
por 24.11.2012 / 16:02