Use sed para localizar e manter apenas as linhas com 2 caracteres alfa neles

2

Eu tenho um arquivo com muitas linhas como estas

33B87401
33B87402
33B87403
33B8EE44
33B87405
33B87406
33B87407
33B87408
33B87409
33B8740A
33B8740B
33B8740C
33B87D0D
33B8740E
33B8740F
33B87410
33B87411
33B87C1E
33B87CC3
33B87C1C

Estou procurando uma maneira de manter apenas as linhas com apenas dois caracteres alfa

saída para este exemplo seria

33B8740A
33B8740B
33B8740C
33B8740E
33B8740F

aqui está outra lista

8765C3E3
8765C3E4
8765C3E5
8765C3E6
8765C3E7
8765C3E8
8765C3E9
8765C3EA
8765C3EB
8765C3EC
8765C3ED
8765C3EE
8765C3EF
8765C3F0

Leia muitos exemplos de sed e awk e non podem reproduzir isso.

Obrigado

    
por user2892743 27.12.2015 / 23:30

6 respostas

3

sed -ne's/[[:alpha:]]//3;t' -e's//&/2p'  <in >out

... irá s/// ubstituir a terceira ocorrência em uma linha de qualquer caractere na classe [[:alpha:]] . Posteriormente, t ests se essa substituição foi bem-sucedida e, em caso afirmativo, ramifica-se fora do script.

Como sed é instruído a -n ot a imprimir automaticamente, as linhas de entrada com três ou mais caracteres alfabéticos são efetivamente excluídas da saída e as únicas linhas de entrada restantes para a segunda instrução s/// ubstitution são aquelas com dois ou menos caracteres alfabéticos.

A segunda substituição usa o vazio // regex no lado esquerdo - que, para sed , (mais eficientemente) refere-se ao mais recentemente compilado / regexp / - e assim pode ser lido como s/[[:alpha:]]/... mais uma vez. Este tenta s/// ubstitute a segunda ocorrência de um caractere alfabético em uma linha para & em si - e, como tal, resulta em um noop efetivo e nenhuma modificação real na linha. No entanto, se conseguir fazer isso, a linha também será p rinted para a saída.

Em resumo - o primeiro s/// ubstitution efetivamente exclui da saída todas as linhas de entrada que correspondem a três ou mais caracteres alfabéticos, e a segunda substituição p rints do que resta para a saída apenas aqueles que correspondem a dois alfabéticos. >

... w / grep ...

grep -xE '([0-9]*[[:alpha:]]){2}' <in >out

Esta declaração não faz exatamente como pedida. Ele seleciona de entrada apenas as linhas que são compostas de apenas alfanuméricos, e desse subconjunto apenas aquelas que não correspondam a mais ou menos de dois alfabéticos, o segundo dos quais deve ser o último caractere. Essa declaração funcionará para produzir o exemplo de saída desejado da entrada de exemplo.

Para fazer como solicitado, porém:

grep -xE '([^[:alpha:]]*[[:alpha:]]){2}[^[:alpha:]]*'

Essa instrução selecionará linhas de entrada que não correspondam a mais ou a menos do que dois caracteres alfabéticos que possam ser encontrados em qualquer posição em uma linha de entrada e que possam ser separados por qualquer número de caracteres alfabéticos ^ .

A opção grep do -x é usada nos dois casos. Entretanto, qualquer instrução pode dispensar se as âncoras ^ head-of-line e $ tail-of-line fossem (pre | ap) pendentes para o regexp, respectivamente. A opção -x denota uma correspondência linha inteira - e, portanto, o regexp deve descrever todas as linhas de entrada correspondidas inteiramente, da cabeça à ponta.

    
por 28.12.2015 / 01:14
2

Eu usaria perl :

perl -ne 'print if length s/\d//gr == 2'

Quais usos:

  • -n para envolver implicitamente um while ( <> ) { loop
  • s///r retorna o texto substituído, mas não modifica o original.
  • , por isso, removemos todos os dígitos e, em seguida, analisamos o comprimento da string
  • e imprima a linha se for 2.

Nota: isso remove os dígitos das suas linhas, deixando não dígitos. Você poderia usar [^A-Z] em vez disso.

Ou alternativamente - se estiver mais claro:

perl -ne 'print if (()=m/([A-Z])/g) == 2'

Isso funcionará com o perl mais antigo, que não suporta o r flag. Ele usa correspondência de expressão regular para selecionar texto e conta o número de elementos da matriz (correspondências). E se isso for 2, imprime a linha.

    
por 28.12.2015 / 00:33
1

eu apenas escrevi um script python simples que faz exatamente o que você quer, eu testei em sua entrada, ele funciona bem.

   #!/usr/bin/python

   def count_letters(input):
     count=0
     for char in input:
       if char.isalpha():
         count += 1
     return count

  fh=open('test_input','r')
  for line in fh.readlines():
    if count_letters(line) == 2 :
      print line
    
por 28.12.2015 / 00:54
1

Algo ao longo das linhas:

grep '^[0-9]*[A-F][0-9]*[A-F][0-9]*$'

deve fazer o truque.

Isso significa que um padrão que começa no início da linha tem alguns (talvez nenhum) dígito, uma letra, mais dígitos, outra letra e mais dígitos antes do final da linha. As âncoras são críticas, caso contrário, você obterá todas as linhas com duas ou mais letras.

    
por 28.12.2015 / 01:29
1

com awk

awk '{x=$0; gsub(/[^[:alpha:]]/, "", x)};length(x) == 2' file

Isso define cada linha em uma variável x , seguida pela substituição de todos os caracteres não alfa em x pela string vazia. Se o comprimento do x assim modificado for igual a 2 , a linha em questão qualifica

Como alternativa, com grep

grep '^[^[:alpha:]]*[:[:alpha:]][^[:alpha:]]*[:[:alpha:]][^[:alpha:]]*$' file
    
por 28.12.2015 / 05:43
0

com grep :

LC_ALL=C grep -E '^([^[:upper:]]*[[:upper:]]){2}[^[:upper:]]*$' file.txt

Exemplo:

% cat file.txt 
33B87401
33B87402
33B87403
33B8EE44
33B87405
33B87406
33B87407
33B87408
33B87409
33B8740A
33B8740B
33B8740C
33B87D0D
33B8740E
33B8740F
33B87410
33B87411
33B87C1E
33B87CC3
33B87C1C

% LC_ALL=C grep -E '^([^[:upper:]]*[[:upper:]]){2}[^[:upper:]]*$' file.txt
33B8740A
33B8740B
33B8740C
33B8740E
33B8740F
    
por 28.12.2015 / 10:34

Tags