O grep pode mostrar apenas palavras que correspondem ao padrão de pesquisa?


685

Existe uma maneira de criar "palavras" de saída grep de arquivos que correspondem à expressão de pesquisa?

Se eu quiser encontrar todas as instâncias de, digamos, "th" em vários arquivos, eu posso fazer:

grep "th" *

mas a saída será algo como (negrito é por mim);

some-text-file: o gato sentou-se no tapete  
algum outro arquivo de texto: a raposa marrom rápida  
contudo-outra-text-file: eu espero que isso explica minuciosamente 

O que eu quero que ele produza, usando a mesma pesquisa, é:

the
the
the
this
thoroughly

Isso é possível usando grep? Ou usando outra combinação de ferramentas?


2
A solução Dan Midwood funciona perfeitamente e merece o crédito.
— hakish

Existe uma maneira de imprimir essas palavras correspondentes sem alterar as linhas. Em vez disso, a string correspondente deve permanecer na mesma linha?
— Linguist

Respostas:


955

Tente grep -o

grep -oh "\w*th\w*" *

Editar: correspondência do comentário de Phil

Dos documentos :

-h, --no-filename
    Suppress the prefixing of file names on output. This is the default
    when there is only  one  file  (or only standard input) to search.
-o, --only-matching
    Print  only  the matched (non-empty) parts of a matching line,
    with each such part on a separate output line.

9
@ user181548, A opção grep -o funciona apenas no GNU grep. Portanto, se você não estiver usando o GNU grep, pode não funcionar para você.
— precisa saber é o seguinte

5
@ABB Depende se você deseja exibir o nome do arquivo correspondente ou não. Não tenho certeza de quais condições ele exibe ou não, mas sei que quando usei grep em vários diretórios, ele exibia o caminho completo do arquivo para todos os arquivos correspondentes, enquanto que com -h, apenas exibia o palavras correspondentes sem qualquer especificação sobre qual arquivo é. Portanto, para corresponder à pergunta original, acho que é necessário em determinadas circunstâncias.
— LokMac

1
Eu precisava de uma explicação para o que "\w*th\w*" *significa, então achei que iria postar. \wé [_ [: alnum:]], então isso corresponde basicamente a qualquer "palavra" que contenha 'th' (já \wque não inclui espaço). O * após a seção citada é uma bola para o qual os arquivos (ou seja, combinando todos os arquivos neste diretório)
— jeremysprofile

1
\wgeralmente não é portátil para grep -E; para portabilidade adequada, use o nome da classe de caracteres POSIX [[:alnum:]](ou [_[:alnum:]]se você realmente deseja o sublinhado também; ou tentegrep -P se sua plataforma possui esse).
— Tripleee

@ABB Dada a saída desejada mostrada pelo OP, -hé totalmente necessário, eu diria ..?
— El Ronnoco

81

Resposta segura de distribuição cruzada (incluindo windows minGW?)

grep -h "[[:alpha:]]*th[[:alpha:]]*" 'filename' | tr ' ' '\n' | grep -h "[[:alpha:]]*th[[:alpha:]]*"

Se você estiver usando versões mais antigas do grep (como 2.4.2), que não inclui a opção -o. Use o acima. Caso contrário, use o mais simples para manter a versão abaixo.

Resposta segura para distribuição cruzada do Linux

grep -oh "[[:alpha:]]*th[[:alpha:]]*" 'filename'

Para resumos, -oha expressão regular corresponde ao conteúdo do arquivo (e não ao nome do arquivo), exatamente como você esperaria que a expressão regular funcionasse no vim / etc ... Qual palavra ou expressão regular você procuraria então, depende de você! Enquanto você permanecer no POSIX e não na sintaxe perl (consulte abaixo)

Mais do manual para grep

-o      Print each match, but only the match, not the entire line.
-h      Never print filename headers (i.e. filenames) with output lines.
-w      The expression is searched for as a word (as if surrounded by
         `[[:<:]]' and `[[:>:]]';

A razão pela qual a resposta original não funciona para todos

O uso de \wvaria de plataforma para plataforma, pois é uma sintaxe "perl" estendida. Como tal, a instalação grep limitada a trabalhar com classes de caracteres POSIX usa [[:alpha:]]e não seu equivalente em perl \w. Veja a página da Wikipedia sobre expressões regulares para mais

Por fim, a resposta POSIX acima será muito mais confiável, independentemente da plataforma (sendo a original) para grep

Quanto ao suporte à opção grep sem -o, o primeiro grep gera as linhas relevantes, o tr divide os espaços em novas linhas, o grep final filtra apenas as respectivas linhas.

(PS: Eu sei que a maioria das plataformas até agora teria sido corrigida por \ w .... mas sempre há aquelas que ficam para trás)

Crédito pela solução alternativa "-o" da resposta @AdamRosenfield


1
E o -o trabalhando apenas no GNU grep (como o ksinkar mencionado em um comentário na resposta aceita)?
— Brilliand

@ Brilliand hmm, estou tendo problemas para encontrar uma implementação linux que não suporte '-o', posso procurar uma solução alternativa se souber qual plataforma verificar.
— PicoCreator

@pico A -oopção não está presente nas janelas grep que é instalada com o pacote git (MinGW?): "c:\Program Files (x86)\Git\bin\grep" --version grep (GNU grep) 2.4.2
— Bruce Peterson

@BrucePeterson que adicionei na resposta da solução alternativa do AdamRosenfield para -o: Ajude-me a verificar se o windows git inclui tr / sed e sua versão. Para que eu possa verificar se esta solução alternativa funciona
— PicoCreator

@pico: para GIT: GNU sed versão 4.2.1, tr (textutils GNU) 2.0
— Bruce Peterson

46

É mais simples do que você pensa. Tente o seguinte:

egrep -wo 'th.[a-z]*' filename.txt #### (Case Sensitive)

egrep -iwo 'th.[a-z]*' filename.txt  ### (Case Insensitive)

Onde,

 egrep: Grep will work with extended regular expression.
 w    : Matches only word/words instead of substring.
 o    : Display only matched pattern instead of whole line.
 i    : If u want to ignore case sensitivity.

2
Parece que isso não adiciona nada sobre as respostas existentes de mais de quatro anos antes.
— Tripleee

3
Tripleee @ eu achei minha abordagem é melhor e simples, então eu postei isso.
— Abhinandan prasad

42

Você pode converter espaços em novas linhas e depois grep, por exemplo:

cat * | tr ' ' '\n' | grep th

18
não precisa de gato. tr '' '\ n' <arquivo | grep th. Lento para arquivos grandes.
— ghostdog74

Isso não funcionou. A saída ainda continha o nome do arquivo e toda a linha do arquivo que continha a correspondência. De qualquer forma, uma das outras soluções oferecidas funcionou. Obrigado pela contribuição embora.
— 10249 Neil Baldwin

@ ghostdog74: bom ponto, embora se você tiver mais do que arquivo, precisará usar o gato. @ Neil Baldwin: você tem certeza de que digitou certo? Quando há apenas um arquivo de entrada (stdin nesse caso), grep não imprime o nome do arquivo.
— 9339 Adam Rosenfield

@ Adam - sim, desculpe Adam, ele funciona com um arquivo, mas não com múltiplos.
— 306 Neil Baldwin

4
@ ghostdog74 se a parte lenta é por causa disso tr, ele poderia fazer grepprimeiro, então trseria aplicado apenas às linhas correspondentes:grep th filename | tr ' ' '\n' | grep th
— Carcamano

37

Apenas awk, não há necessidade de combinação de ferramentas.

# awk '{for(i=1;i<=NF;i++){if($i~/^th/){print $i}}}' file
the
the
the
this
thoroughly

8
@AjeetGanga bem, é no nome
— Daerdemandt

11

comando grep apenas para correspondência e perl

grep -o -P 'th.*? ' filename

3
E a exibição apenas do grupo correspondente?
— Bishwas Mishra

Isso não funciona; ele só será encontrado thporque você solicitou a menor repetição possível do curinga.
— Tripleee

@ tripleee - não terá esse problema, porque há um espaço incluído no final do regex. No entanto, faltam palavras que não têm espaços após elas, por exemplo, no final das linhas.
— Ken Williams

8

Eu estava insatisfeito com a sintaxe difícil de lembrar do awk, mas gostei da ideia de usar um utilitário para fazer isso.

Parece que o ack (ou o ack-grep, se você usa o Ubuntu) pode fazer isso facilmente:

# ack-grep -ho "\bth.*?\b" *

the
the
the
this
thoroughly

Se você omitir o sinalizador -h, obtém:

# ack-grep -o "\bth.*?\b" *

some-other-text-file
1:the

some-text-file
1:the
the

yet-another-text-file
1:this
thoroughly

Como bônus, você pode usar o --outputsinalizador para fazer isso em pesquisas mais complexas com a sintaxe mais fácil que encontrei:

# echo "bug: 1, id: 5, time: 12/27/2010" > test-file
# ack-grep -ho "bug: (\d*), id: (\d*), time: (.*)" --output '$1, $2, $3' test-file

1, 5, 12/27/2010


4

Para pesquisar todas as palavras com start com "icon-", o comando a seguir funciona perfeitamente. Estou usando o Ack aqui, que é semelhante ao grep, mas com melhores opções e boa formatação.

ack -oh --type=html "\w*icon-\w*" | sort | uniq

3

Você também pode tentar o pcregrep . Há também uma -wopção no grep , mas em alguns casos não funciona como o esperado.

Da Wikipedia :

cat fruitlist.txt
apple
apples
pineapple
apple-
apple-fruit
fruit-apple

grep -w apple fruitlist.txt
apple
apple-
apple-fruit
fruit-apple

3

Eu tive um problema semelhante, procurando por regex grep / pattern e o "padrão correspondente encontrado" como saída.

No final, usei o egrep (o mesmo regex no grep -e ou -G não me deu o mesmo resultado do egrep) com a opção -o

então, eu acho que poderia ser algo semelhante a (eu não sou um mestre de regex):

egrep -o "the*|this{1}|thoroughly{1}" filename

Os {1}quantificadores inúteis devem ser descartados. Ou se você quiser ser consistente, t{1}h{1}e{1}etc. #
— tripleee 21/03

ele pode imprimir com a mesma linha?
— 吴毅凡

-1

Você pode canalizar sua saída grep para Perl assim:

grep "th" * | perl -n -e'while(/(\w*th\w*)/g) {print "$1\n"}'

9
isso não dará o resultado correto. Além disso, se estiver usando Perl, não há necessidade de usar grep. faça tudo em Perl.
— ghostdog74

Obrigado por apontar o erro, ghostdog74. Eu mudei para imprimir todas as palavras da linha, não apenas a primeira.

como eu disse, grep não é necessário. perl -n -e' while (/ (\ s + th \ w *) / g) {print "$ 1 \ n"} 'arquivo
— ghostdog74 10/10/09

7
você decide. estou apenas ilustrando um ponto. Se não for necessário, não faça. esse extra "|" custará um processo a mais.
— ghostdog74

1
No Perl 5.10 ou posterior: perl -nE '@a = / (regexp) / ig; dizer junte-se "\ n", @a '
— Professor Photon

-1
$ grep -w

Trecho da página do manual grep:

-w: selecione apenas as linhas que contêm correspondências que formam palavras inteiras. O teste é que a substring correspondente deve estar no início da linha ou precedida por um caractere que não seja de palavra.


1
Isso ainda imprimirá a linha inteira que contém a correspondência. Ele restringe a correspondência real para que thenão corresponda mais, por exemplo, "estes" ou "banhar".
— Tripleee

-6

ripgrep

Aqui está o exemplo usando ripgrep:

rg -o "(\w+)?th(\w+)?"

Combina todas as palavras correspondentes th.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.