O piped grep aninhado resulta em uma string dizendo “(entrada padrão)” [duplicado]

4

Estou realizando um grep aninhado assim:

grep -ir "Some string" . |grep "Another string I want to find in the other grep's results"

Isso funciona perfeitamente como pretendido (eu recebo os resultados do primeiro grep filtrado pelo segundo grep também), mas assim que eu adiciono uma opção "-l" então eu só obtenho a lista de arquivos do segundo grep , Eu não entendo nada.

grep -ir "Some string" . |grep -l "Another string I want to find in the other grep's results"

Isso resulta na seguinte saída:

(standard output)

Eu acho que a tubulação não funciona quando eu quero apenas a lista de arquivos. Alguma alternativa?

    
por OMA 20.04.2017 / 13:32

5 respostas

3

use "cut" para remover strings depois de ":" e então você obtém as partes do arquivo (assumindo que os caminhos dos arquivos não contêm dois-pontos ou caracteres newline e não correspondem ao segundo padrão).

grep -ir "Some string" . |grep "Another string I want to find in the other grep's results" | cut -d ":" -f 1

em caso de duplicação use "uniq"

grep -ir "string1" . | grep "string2" | cut -d: -f1 | uniq
    
por 20.04.2017 / 13:58
13

A opção -l para grep fará com que o utilitário imprima apenas o nome do arquivo que contém o padrão especificado. O manual no meu sistema diz o seguinte sobre esta opção:

Only the names of files containing selected lines are written to standard output. grep will only search a file until a match has been found, making searches potentially less expensive. Pathnames are listed once per file searched. If the standard input is searched, the string "(standard input)" is written.

Como o segundo grep em seu pipeline está lendo da entrada padrão, não de um arquivo, ele não sabe de onde os dados estão vindo, diferente do que está chegando em seu fluxo de entrada padrão. É por isso que está retornando a string de texto (standard input) (não (standard output) como está escrito na pergunta). Isso é o mais próximo possível de chegar onde a partida estava localizada.

Para combinar os dois padrões no primeiro grep (que tem tem conhecimento sobre quais arquivos ele está procurando), veja " Como executar o grep com vários padrões AND? "

    
por 20.04.2017 / 13:46
5

(Suponho que você pretendia que o segundo grep correspondesse ao conteúdo da linha e não aos nomes dos arquivos, ou ambos, conforme sua abordagem estava sendo feita)

POSIXly:

find . -type f -exec awk '
  FNR == 1 {found = 0}
  !found && tolower($0) ~ /some string/ && /other string/ {
    print FILENAME
    found = 1
    nextfile
  }' {} +

A empresa sobre found é para implementações awk que não suportam nextfile ainda (e onde nextfile é, então, um não operacional). Se você souber que sua implementação de awk suporta nextfile , você poderá simplificá-la para:

 find . -type f -exec awk 'tolower($0) ~ /some string/ && /other string/ {
    print FILENAME; nextfile}' {} +

Com o GNU grep construído com suporte a PCRE, uma vez que você deseja que uma correspondência seja insensível a maiúsculas e minúsculas e não a outra:

grep -rlP '^(?=.*(?i:some string))(?=.*other string)' .

(?=...) é um operador perl look-ahead . (?i:pattern) ativa a correspondência sem distinção entre maiúsculas e minúsculas apenas para pattern . Então, aqui estamos combinando no início da linha ( ^ ), desde que seja seguido por qualquer número de caracteres ( .* ) seguido por some string (insensível a maiúsculas) e também que ele (o início da linha) é seguido por qualquer número de caracteres e other string (diferencia maiúsculas de minúsculas).

Se o grep não der suporte a -P , você poderá ter o comando pcregrep (substitua grep -rlP por pcregrep -rl ) ou, se os padrões não se sobrepuserem, você poderá fazer:

grep -rl -e '[sS][oO][mM][eE] [sS][tT][rR][iI][nN][gG].*other string' \
         -e 'other string.*[sS][oO][mM][eE] [sS][tT][rR][iI][nN][gG]' .

Ou se você não quer que as duas correspondências sejam insensíveis a maiúsculas e minúsculas:

grep -ril -e 'some string.*other string' \
          -e 'other string.*some string' .
    
por 20.04.2017 / 14:08
2

Você pode colocar os dois paternos em uma consulta (com base neste answer ) usando o seguinte padrão:

grep -P '^(?=.*pattern1)(?=.*pattern2)'

No seu caso, você pode adicionar os parâmetros -ir -l da seguinte forma:

grep -irlP '^(?=.*pattern1)(?=.*pattern2)' .
    
por 20.04.2017 / 13:51
0

Esta é a solução mais curta de todas as fornecidas.

find . -type f -exec perl -lne '
   /Some string/i and /other string/ and print($ARGV),close(*ARGV);
' {} +
grep -irZ "Some string" . |
perl -lsF'/\n/' -0ne '
   s/^/\n/ if $. == 1; s/$/\n/ if eof;

   $. == 1 and $prev = $F[1],next;
   push @{$h{$prev}}, $F[0];
   $prev = $F[1];

   END {
      grep($_ =~ /\Q${str2}/, @{$h{$_}}) and print for keys %h;
   }
' -- -str2="Another string"

Princípio de funcionamento: Aqui, o primeiro grep faz uma recursive , case-insensitive procurar por "alguma sequência" no diretório atual e para baixo e gera registros separados por nulo ( -Z ) devido à opção grep dada a Perl .

Cada um desses registros contém o nome do arquivo e a linha correspondente. Apenas o engate é que a ordenação não está em andamento, devido ao comportamento do grep de não colocar um \n após a linha correspondente. Para contornar essa limitação, usamos %code% lendo registros separados por nulo e dividindo esses registros em %code% para separar as linhas dos nomes de arquivos.

Por isso, não há limitação quanto aos tipos de nomes de arquivos que podem estar envolvidos, com exceção de %code% , que, de qualquer forma, são proibidos.

    
por 20.04.2017 / 13:46

Tags