Localizando texto entre dois caracteres específicos ou cadeias de caracteres

16

Digamos que eu tenha linhas como esta:

*[234]*
*[23]*
*[1453]*

em que * representa qualquer string (exceto uma string no formato [number] ). Como posso analisar essas linhas com um utilitário de linha de comando e extrair o número entre colchetes?

Em geral, quais dessas ferramentas cut , sed , grep ou awk seriam apropriadas para essa tarefa?

    
por Amelio Vazquez-Reina 07.03.2012 / 21:05

4 respostas

16

Se você tiver o grep do GNU, você pode usar sua opção -o para procurar por um regex e produzir apenas a parte correspondente. (Outras implementações do grep podem mostrar apenas a linha inteira.) Se houver várias correspondências em uma linha, elas serão impressas em linhas separadas.

grep -o '\[[0-9]*\]'

Se você quer apenas os dígitos e não os colchetes, é um pouco mais difícil; você precisa usar uma asserção de largura zero: um regexp que corresponda à string vazia, mas somente se for precedido ou seguido conforme o caso, por um colchete. As asserções de largura zero estão disponíveis apenas na sintaxe Perl.

grep -P -o '(?<=\[)[0-9]*(?=\])'

Com sed, você precisa desativar a impressão com -n e corresponder à linha inteira e reter apenas a parte correspondente. Se houver várias correspondências possíveis em uma linha, somente a última correspondência será impressa. Consulte Como extrair um regex correspondido com 'sed' sem imprimir os caracteres adjacentes para obter mais detalhes sobre o uso do sed aqui.

sed -n 's/^.*\(\[[0-9]*\]\).*//p'

ou se você quiser apenas os dígitos e não os colchetes:

sed -n 's/^.*\[\([0-9]*\)\].*//p'

Sem grep -o , o Perl é a ferramenta escolhida aqui se você quiser algo simples e compreensível. Em cada linha ( -n ), se a linha contiver uma correspondência para \[[0-9]*\] , imprima essa correspondência ( $& ) e uma nova linha ( -l ).

perl -l -ne '/\[[0-9]*\]/ and print $&'

Se você quiser apenas os dígitos, coloque parênteses na expressão regular para delimitar um grupo e imprima apenas esse grupo.

perl -l -ne '/\[([0-9]*)\]/ and print $1'

P.S. Se você quiser apenas exigir um ou mais dígitos entre os colchetes, altere [0-9]* para [0-9][0-9]* ou para [0-9]+ em Perl.

    
por 08.03.2012 / 00:29
5

Você não pode fazer isso com cut .

  1. tr -c -d '01234567892'
  2. sed 's/[^0-9]*//g'
  3. awk -F'[^0-9]+' '{ print $1$2$3 }'
  4. grep -o -E '[0-9]+'

tr é o ajuste mais natural para o problema e provavelmente seria o mais rápido, mas acho que você precisaria de entradas gigantescas para separar qualquer uma dessas opções em termos de velocidade.

    
por 07.03.2012 / 22:53
4

Se você quer extrair um conjunto de dígitos consecutivos entre caracteres não digitados, acho que sed e awk são os melhores (embora grep também seja capaz de fornecer os caracteres correspondentes):

sed : é claro que você pode combinar os dígitos, mas talvez seja interessante fazer o oposto, remover os não-dígitos (funciona até um único número por linha):

$ echo nn3334nn | sed -e 's/[^[[:digit:]]]*//g'
3344

grep : você pode combinar dígitos consecutivos

$ echo nn3334nn | grep -o '[[:digit:]]*'
3344

Eu não dou um exemplo para awk porque eu tenho experiência nula com ele; É interessante notar que, embora sed seja uma faca suíça, grep oferece uma maneira mais simples e mais legível de fazer isso, o que também funciona para mais de um número em cada linha de entrada ( -o imprime apenas as partes correspondentes da entrada, cada uma na sua própria linha):

$ echo dna42dna54dna | grep -o '[[:digit:]]*'
42
54
    
por 07.03.2012 / 21:23
2

Já que foi dito que isso não pode ser feito com cut , mostrarei que é possível produzir uma solução que seja pelo menos não pior do que algumas das outras, mesmo que eu não endosse o uso de cut como a solução "melhor" (ou mesmo particularmente boa). Deve-se dizer que qualquer solução que não procure especificamente por *[ e ]* em torno dos dígitos torna hipóteses simplificadoras e, portanto, propensa a falhas em exemplos mais complexos do que aquele dado pelo consulente (por exemplo, dígitos fora de *[ e ]* , que não deve ser mostrado). Essa solução verifica pelo menos os colchetes e pode ser estendida para verificar os asteriscos também (deixada como um exercício para o leitor):

cut -f 2 -d '[' myfile.txt | cut -f 1 -d ']'

Isso faz uso da opção -d , que especifica um delimitador. Obviamente, você também pode canalizar para a expressão cut em vez de ler um arquivo. Embora cut seja provavelmente muito rápido, já que é simples (sem mecanismo regex), é necessário invocá-lo pelo menos duas vezes (ou mais algum tempo para verificar por * ), o que cria alguma sobrecarga de processo. A única vantagem real dessa solução é que ela é bastante legível, especialmente para usuários casuais que não são bem versados em construções de expressão regular.

    
por 24.01.2013 / 18:04