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?

Neil Baldwin
fonte
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.
Dan Midwood
fonte
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

PicoCreator
fonte
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.
Abhinandan prasad
fonte
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
Adam Rosenfield
fonte
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
ghostdog74
fonte
8
@AjeetGanga bem, é no nome
Daerdemandt
11

comando grep apenas para correspondência e perl

grep -o -P 'th.*? ' filename
Raghu
fonte
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
Beau
fonte
8
cat *-text-file | grep -Eio "th[a-z]+"
Mumbling Mac
fonte
2
ou apenas grep -Eio "th [az] +" nome do arquivo
Shayan
3
Talvez veja também Uso inútil de cat?
Tripleee
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
Sandeep
fonte
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
Maciek Sawicki
fonte
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
keebOo
fonte
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"}'

fonte
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.

pl1nk
fonte
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.

kenorb
fonte