Por que 'grep -q' consome todo o arquivo de entrada?

23

Considere o seguinte arquivo de entrada:

1
2
3
4

Corrida

{ grep -q 2; cat; } < infile

não imprime nada. Eu esperava que fosse impresso

3
4

Posso obter a saída esperada se a alterar para

{ sed -n 2q; cat; } < infile

Por que o primeiro comando não imprime a saída esperada?
É um arquivo de entrada procurável e de acordo com o padrão em OPTIONS :

-q
      Quiet. Nothing shall be written to the standard output, regardless of 
      matching lines. Exit with zero status if an input line is selected.

e mais abaixo, em USO DO APLICATIVO (enfatize o meu):

A -qopção fornece um meio de determinar facilmente se um padrão (ou sequência) existe ou não em um grupo de arquivos. Ao pesquisar vários arquivos, ele fornece uma melhoria de desempenho ( porque pode sair assim que encontrar a primeira correspondência ) [...]

Agora, pelo mesmo padrão (em Introdução , em INPUT FILES )

Quando um utilitário padrão lê um arquivo de entrada procurável e termina sem erro antes de chegar ao final do arquivo, o utilitário deve garantir que o deslocamento do arquivo na descrição do arquivo aberto seja posicionado corretamente logo após o último byte processado pelo utilitário [. ..]

tail -n +2 file
(sed -n 1q; cat) < file
...

O segundo comando é equivalente ao primeiro apenas quando o arquivo é procurável.


Por que grep -qconsome o arquivo inteiro?


Isso é gnu grepimportante (embora o Kusalananda tenha confirmado o mesmo no OpenBSD)

don_crissti
fonte
O OpenBSD's grepé um fork de algo chamado FreeGrep , se alguém se perguntar.
Kusalananda

Respostas:

37

grep pára cedo, mas armazena em buffer suas entradas, para que seu teste seja muito curto (e sim, percebo que meu teste é imperfeito, pois não é possível procurar):

seq 1 10000 | (grep -q 2; cat)

começa em 6776 no meu sistema. Isso corresponde ao buffer de 32KiB usado por padrão no GNU grep:

seq 1 6775 | wc

saídas

   6775    6775   32768

Observe que o POSIX menciona apenas melhorias de desempenho

Ao pesquisar vários arquivos

Isso não cria expectativas para melhorias de desempenho devido à leitura parcial de um único arquivo.

Stephen Kitt
fonte
2

Obviamente, isso se deve ao buffer que grepfaz para acelerar as coisas. Existem ferramentas projetadas especificamente para ler quantos caracteres forem solicitados e não mais. Um deles é expect:

{ expect -c "log_user 0; expect 2"; cat; } < infile

Eu não tenho um sistema para testar isso, mas acredito que expectirá consumir tudo até encontrar a string esperada ( 2) e terminar, deixando o restante da entrada para cat.

Dmitry Grigoryev
fonte
1

Você está confundindo sed e grep.

Para o comando sed, -2qestá dizendo para sair da iteração atual, se na segunda linha, a -nopção está dizendo para funcionar silenciosamente, para que você obtenha todas as linhas após a 2ª.

O comando grep é executado por padrão para gerar todas as linhas correspondentes - mas a -qopção diz para não gerar nada para stdout. portanto, se a entrada contiver um "2", ela terá um valor de saída SUCCESS, caso contrário FAILURE. O que são depende do seu sistema operacional e shell. Portanto, normalmente você diria se uma linha corresponde examinando o valor de saída do processo grep. Isso é útil em um pipeline no qual você deseja saber se sua entrada contém algum valor como teste. Por exemplo

if grep -q 'crash' <somelog.log ; then report_crash_via_email ; fi

Nesse caso, não gostamos de ver todas as linhas correspondentes, apenas nos importamos se houver pelo menos uma. O report_crash_via_emailprocesso / função pode então sair e reabrir o arquivo, ou não.

Se você deseja que seu processo grep PARE depois de encontrar o caractere "2" - ele não será por padrão, ele inspecionará todas as linhas procurando ver se elas correspondem - você precisa dizer para fazer isso. A opção de linha de comando para isso é -m <value>. Então, para o seu caso grep -q -m1 2.

user212377
fonte
6
Sua resposta é uma informação útil para uso geral, grepmas esta pergunta é sobre algo mais sutil e esotérico. Parece que você leu a pergunta muito rapidamente para entender o comportamento real que está sendo consultado. Além disso, GNU grep faz parar a busca quando usado com -q(conforme permitido na citação a partir da especificação POSIX): A página man para os estados do GNU grep que “Exit [s] imediatamente com status de zero se qualquer correspondência for encontrada” . FWIW, editei sua pergunta para mostrar como você pode formatar postagens futuras. Welcom para Stack Exchange .
Anthony G - justiça para Monica
Dito isto, a resposta do @ user212377 está correta: neste caso, grepestá sendo perguntado se '2' existe no arquivo, nada mais e nada menos. Ele não se comporta sede consome registros até esse ponto e deixa o restante para processamento adicional. Ele lê até saber que existe um '2' ou não, fecha o arquivo e retorna o resultado.
Keith Davies
grepna verdade, apenas 'consome o arquivo inteiro' (ignorando considerações de buffer) se a cadeia de pesquisa não estiver presente no arquivo (o que é possível apenas examinando o arquivo inteiro). Qualquer coisa menos que isso, a leitura do arquivo é interrompida , o arquivo é fechado e SUCCESS retornado.
Keith Davies