Localizando texto entre dois caracteres ou seqüências específicas

17

Digamos que eu tenha linhas como esta:

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

onde *representa qualquer sequência (exceto uma sequência do formulário [number]). Como posso analisar essas linhas com um utilitário de linha de comando e extrair o número entre colchetes?

De modo mais geral, qual destas ferramentas cut, sed, grepou awkseria apropriado para essa tarefa?

Amelio Vazquez-Reina
fonte

Respostas:

16

Se você possui o GNU grep, pode usar sua -oopção para procurar um regex e gerar apenas a parte correspondente. (Outras implementações 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 parênteses, é um pouco mais difícil; você precisa usar uma asserção de largura zero: uma regexp que corresponda à cadeia vazia, mas apenas se for precedida ou seguida, 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 o sed, você precisa desativar a impressão -ne corresponder a linha inteira e reter apenas a parte correspondente. Se houver várias correspondências possíveis em uma linha, apenas a última correspondência será impressa. Consulte Extraindo um regex correspondente a 'sed' sem imprimir os caracteres ao redor para obter mais detalhes sobre o uso de sed aqui.

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

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

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

Sem grep -o, Perl é a ferramenta de escolha aqui, se você deseja 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ê deseja apenas os dígitos, coloque parênteses no regex para delimitar um grupo e imprima apenas esse grupo.

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

PS Se você deseja exigir apenas um ou mais dígitos entre colchetes, mude [0-9]*para [0-9][0-9]*, ou para [0-9]+Perl.

Gilles 'SO- parar de ser mau'
fonte
Tudo bem, exceto que ele quer "extrair o número entre colchetes". Eu acho que "exceto [number]" os meios, exceto[0-9]
Peter.O
1
@ Peter.OI entendeu “exceto [número]” como significando que não há outras partes da linha desse formulário. Mas editei minha resposta para mostrar como imprimir apenas os dígitos, apenas por precaução.
Gilles 'SO- stop be evil' em
1
Essas perldeclarações regex parecem realmente úteis! Eu tenho lido sobre eles depois de ver você usar afirmações para trás e para frente, mesmo em grep (eu havia mudado para o fato de que você pode escolher um mecanismo de expressão regular). Dedicarei um pouco mais de tempo ao regex de perl daqui em diante. Obrigado ... PS .. Acabei de ler man grep... "Isso é altamente experimental e o grep -P pode avisar sobre recursos não implementados." ... Espero que isso não significa instável ... (?)
Peter.O
5

Você não pode fazer isso com cut.

  1. tr -c -d '0123456789\012'
  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.

Kyle Jones
fonte
Para sed, ^.*é ganancioso e consome tudo, exceto o último dígito, e +precisa ser \+ou então use o posix \([0-9][0-9]*\)... e, de qualquer forma, 's/[^0-9]*//g'funciona da mesma forma, ... Thanks for the exemplo tr -c`, mas isso não é \012excessivo?
precisa saber é o seguinte
@ Peter Obrigado por capturar isso. Eu teria jurado que testei o exemplo sed. :( Eu mudei-o para a sua versão Em relação. \012: Ela é necessária caso contrário trvai comer as novas linhas.
Kyle Jones
Aha ... Eu estava vendo-a como \0, 1, 2(ou mesmo \, 0, 1, 2). Não estou bem sintonizado com octal, parece .. Obrigado.
precisa saber é o seguinte
4

Se você quer dizer extrair um conjunto de dígitos consecutivos entre caracteres não-dígito, eu acho sede awksão a melhor (embora greptambém é capaz de dar-lhe os caracteres casada):

sed: é claro que você pode corresponder os dígitos, mas talvez seja interessante fazer o oposto, remover os não dígitos (funciona na medida em que houver apenas um 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

Não dou um exemplo awkporque tenho experiência nula com ele; é interessante notar que, embora sedseja uma faca suíça, grepoferece uma maneira mais simples e legível de fazer isso, que também funciona para mais de um número em cada linha de entrada (a -oúnica imprime as partes correspondentes da entrada, cada uma em sua própria linha):

$ echo dna42dna54dna | grep -o '[[:digit:]]*'
42
54
njsg
fonte
Apenas como comparação, aqui está uma sedeqivalent do "mais de um número por linha" exemplo grep -o '[[:digit:]]*'. . . sed -nr '/[0-9]/{ s/^[^[0-9]*|[^0-9]*$//g; s/[^0-9]+/\n/g; p}'... (+1)
Peter.O
2

Como se disse que isso não pode ser feito cut, mostrarei que é facilmente possível produzir uma solução que seja pelo menos não pior que algumas outras, mesmo que eu não apóie o uso cutcomo o "melhor". (ou mesmo uma solução particularmente boa). Deve-se dizer que qualquer solução que não procure especificamente *[e em ]*torno dos dígitos faz suposições simplificadoras e, portanto, está sujeita a falhas em exemplos mais complexos do que os fornecidos pelo solicitante (por exemplo, dígitos externos *[e ]*que não devem ser mostrados). Essa solução verifica pelo menos os colchetes e pode ser estendida para verificar também os asteriscos (deixados como um exercício para o leitor):

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

Isso utiliza a -dopção the , que especifica um delimitador. Obviamente, você também pode inserir a cutexpressão em vez de ler de um arquivo. Embora cutseja provavelmente muito rápido, uma vez que é simples (sem mecanismo de regex), você deve invocá-lo pelo menos duas vezes (ou mais um pouco para verificar *), o que cria alguma sobrecarga no processo. A única vantagem real dessa solução é que ela é legível, especialmente para usuários casuais que não são versados ​​em construções regex.

Thomas
fonte