Não precisa de toda a linha, apenas a correspondência da expressão regular

13

Eu simplesmente preciso obter a correspondência de uma expressão regular:

$ cat myfile.txt | SOMETHING_HERE "/(\w).+/"

A saída deve ser apenas o que foi correspondido, dentro dos parênteses.

Não pense que posso usar o grep porque corresponde à linha inteira.

Por favor me deixe saber como faz isso.

Alex L
fonte

Respostas:

11

2 coisas:

  • Conforme declarado pelo @Rory, você precisa da -oopção, para que apenas a correspondência seja impressa (em vez da linha inteira)
  • Além disso, você precisa da -Popção de usar expressões regulares do Perl, que incluem elementos úteis como Olhar para a frente (?= ) e Olhar para trás (?<= ) , que procuram por peças, mas na verdade não coincidem e as imprimem.

Se você deseja que apenas a parte dentro da parêntese seja correspondida:

grep -oP '(?<=\/\()\w(?=\).+\/)' myfile.txt

se o arquivo contiver a picada /(a)5667/, o grep imprimirá 'a', porque:

  • /(são encontrados por \/\(, mas, como estão olhando para trás (?<= ) , não são relatados
  • aé correspondido por \we, portanto, é impresso (por causa de -o)
  • )5667/são encontrados b < \).+\/, mas como eles estão olhando para o futuro, (?= ) eles não são relatados
DrYak
fonte
17

Use a -oopção em grep.

Por exemplo:

$ echo "foobarbaz" | grep -o 'b[aeiou]r'
bar
Rory
fonte
4
Que pena ... Você tem alguma idéia de quantas vezes eu lutei com referências anteriores sedpara fazer isso?
INSYTE
9
A opção o para grep / egrep retorna apenas o que corresponde a toda a expressão regular, não apenas o que está em () como ele pediu.
Kyle Brandt
1
No entanto, isso é uma coisa muito boa de se saber:
Kyle Brandt
2
@KyleBrandt: Para combinar apenas uma parte (por exemplo: os parenses) É possível marcar o resto com uma frente olhar ou olhar para trás: (? <=) E (? =)
DrYak
6
    sed -n "s/^.*\(captureThis\).*$/\1/p"

-n      don't print lines
s       substitute
^.*     matches anything before the captureThis 
\( \)   capture everything between and assign it to \1 
.*$     matches anything after the captureThis 
\1      replace everything with captureThis 
p       print it
Joshua
fonte
4

Se você deseja apenas o que está entre parênteses, precisa de algo que suporte a captura de subconjuntos (grupos de captura nomeados ou numerados). Eu não acho que grep ou egrep possam fazer isso, perl e sed can. Por exemplo, com perl:

Se um arquivo chamado foo tiver uma linha, é a seguinte:

/adsdds      /

E você faz:

perl -nle 'print $1 if /\/(\w).+\//' foo

A letra a é retornada. Isso pode não ser o que você deseja. Se você nos dizer com o que está tentando corresponder, poderá obter melhor ajuda. $ 1 é o que foi capturado no primeiro conjunto de parênteses. $ 2 seria o segundo conjunto etc.

Kyle Brandt
fonte
Eu só estava tentando combinar o que está entre parênteses. Parece passá-lo para um script perl ou php pode ser a resposta.
Alex L
4

Como você marcou sua pergunta como bash , além do shell , há outra solução ao lado do grep :

O Bash possui seu próprio mecanismo de expressão regular desde a versão 3.0, usando o =~operador, assim como o Perl.

agora, com o seguinte código:

#!/bin/bash
DATA="test <Lane>8</Lane>"

if [[ "$DATA" =~ \<Lane\>([[:digit:]]+)\<\/Lane\> ]]; then
        echo $BASH_REMATCH
        echo ${BASH_REMATCH[1]}
fi
  • Observe que você deve invocá-lo como bashe não apenas shpara obter todas as extensões
  • $BASH_REMATCH dará a string inteira conforme a expressão regular inteira, então <Lane>8</Lane>
  • ${BASH_REMATCH[1]} dará a parte correspondente ao 1º grupo, portanto, apenas 8
DrYak
fonte
Caro @DrYak, espero que você não está analisar XML com regex aqui .. :)
joonas.fi
É ainda pior. Estou analisando uma horrível mistura de dados XML e FASTA (que usam o >símbolo para propósitos totalmente diferentes), conforme divulgada pelo software de alinhamento rápido em grande escala SANSparallel . É claro que ambos os formatos são espalhados sem entrelaçar. Portanto, é impossível jogar alguma biblioteca XML padrão nisso. E eu estou usando o regex Bash neste ponto do código, porque eu só preciso extrair alguns dados, e o 2 regex faz o trabalho muito melhor para mim do que escrever um analisador dedicado para essa bagunça. #LifeInBioinformatics
DrYak
Em outras palavras: há um ponto onde extrair um número único é mais simples de fazer com um Rathan regex que dançar todo o tango XML
DrYak
Hah, entendi! :)
joonas.fi
2

Supondo que o arquivo contenha:

$ cat file
Text-here>xyz</more text

E você deseja o (s) caractere (s) entre >e </, você pode usar:

grep -oP '.*\K(?<=>)\w+(?=<\/)' file
sed -nE 's:^.*>(\w+)</.*$:\1:p' file
awk '{print(gensub("^.*>(\\w+)</.*$","\\1","g"))}' file
perl -nle 'print $1 if />(\w+)<\//' file

Todos imprimirão uma string "xyz".

Se você deseja capturar os dígitos desta linha:

$ cat file
Text-<here>1234</text>-ends

grep -oP '.*\K(?<=>)[0-9]+(?=<\/)' file
sed -E 's:^.*>([0-9]+)</.*$:\1:' file
awk '{print(gensub(".*>([0-9]+)</.*","\\1","g"))}' file
perl -nle 'print $1 if />([0-9]+)<\//' file

Seta
fonte
Para mim, crucial foi perceber que \ d não funciona com sed. Há uma razão para você usar [0-9] + lá. :)
user27432 7/19
@ user27423 Não faz, mas classes de personagens POSIX ( leitura dolorosa , de leitura agradável ) fazer: echo 'Text-<here>1234</text>-ends' | sed -E 's|.*>([[:digit:]]+)<.*|\1|'. Em alguns casos (por exemplo, [0-9]vs. [[:digit:]]), eles não ajudam a legibilidade; em outros, acho que ajudam (por exemplo, [ \t\n\r\f\v]vs. [:space:]).
Samuel Harmer
0

Isso realizará o que você está solicitando, mas não acho que seja o que você realmente deseja. Coloquei a .*parte da frente da regex para comer qualquer coisa antes da partida, mas essa é uma operação gananciosa, portanto, isso só corresponde ao penúltimo \wcaractere na string.

Note que você precisa escapar dos parênteses e dos +.

sed 's/.*\(\w\).\+/\1/' myfile.txt
Chad Huneycutt
fonte