Pourquoi 'grep -q' consomme-t-il l'intégralité du fichier d'entrée?


23

Considérez le fichier d'entrée suivant:

1
2
3
4

Fonctionnement

{ grep -q 2; cat; } < infile

n'imprime rien. Je m'attendrais à ce qu'il s'imprime

3
4

Je peux obtenir la sortie attendue si je la change en

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

Pourquoi la première commande n'imprime-t-elle pas la sortie attendue?
C'est un fichier d'entrée recherché et selon la norme sous 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.

et plus bas, sous UTILISATION DE L'APPLICATION ( mettez l' accent sur le mien):

L' -qoption permet de déterminer facilement si un modèle (ou une chaîne) existe ou non dans un groupe de fichiers. Lors de la recherche de plusieurs fichiers, il offre une amélioration des performances ( car il peut se fermer dès qu'il trouve la première correspondance ) [...]

Maintenant, selon le même standard (dans Introduction , sous INPUT FILES )

Lorsqu'un utilitaire standard lit un fichier d'entrée recherché et se termine sans erreur avant d'atteindre la fin du fichier, l'utilitaire doit s'assurer que l'offset de fichier dans la description du fichier ouvert est correctement positionné juste après le dernier octet traité par l'utilitaire [. ..]

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

La deuxième commande est équivalente à la première uniquement lorsque le fichier est recherché.


Pourquoi grep -qconsomme tout le fichier?


C'est gnu grepsi cela importe (bien que Kusalananda vient de confirmer que la même chose se produit sur OpenBSD)


OpenBSD grepest un fork de quelque chose appelé FreeGrep , si quelqu'un se demande.
Kusalananda

Réponses:


37

grep ne s'arrête pas tôt, mais il met en mémoire tampon son entrée, donc votre test est trop court (et oui, je me rends compte que mon test est imparfait car il n'est pas recherché):

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

commence à 6776 sur mon système. Cela correspond au tampon de 32 Ko utilisé par défaut dans GNU grep:

seq 1 6775 | wc

les sorties

   6775    6775   32768

Notez que POSIX ne mentionne que les améliorations de performances

Lors de la recherche de plusieurs fichiers

Cela ne définit aucune attente d'amélioration des performances en raison de la lecture partielle d'un seul fichier.


2

Cela est évidemment dû à la mise en mémoire tampon qui grepaccélère les choses. Il existe des outils spécialement conçus pour lire autant de caractères que demandé et pas plus. L'un d'eux est expect:

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

Je n'ai pas de système pour essayer cela, mais je crois expectqu'il mangera tout jusqu'à ce qu'il rencontre la chaîne attendue ( 2), puis se terminera, laissant le reste de l'entrée pour cat.


1

Vous confondez sed et grep.

Pour la commande sed, -2qdit de quitter l'itération en cours si à la deuxième ligne, l' -noption dit de fonctionner silencieusement, donc vous obtiendrez toutes les lignes après la 2e.

La commande grep s'exécute par défaut pour sortir toutes les lignes correspondantes - mais l' -qoption dit de ne rien sortir vers stdout. ainsi, si l'entrée contient un "2", elle aura une valeur de sortie SUCCESS, sinon FAILURE. Ce que cela dépend de votre système d'exploitation et de votre shell. Donc, généralement, vous diriez si une ligne correspond en examinant la valeur de sortie du processus grep. Ceci est utile dans un pipeline où vous voulez savoir si votre entrée contient une valeur comme test. Par exemple

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

Dans ce cas, nous ne nous soucions vraiment pas de voir toutes les lignes correspondantes, nous nous soucions simplement si au moins une existe. Le report_crash_via_emailprocessus / la fonction peut alors s'éteindre et rouvrir le fichier, ou non.

Si vous voulez que votre processus grep s'arrête après avoir trouvé le caractère "2" - il ne le fera pas par défaut, il inspectera chaque ligne pour voir s'il correspond - vous devez lui dire de le faire. Le commutateur de ligne de commande pour cela est -m <value>. Donc pour votre cas grep -q -m1 2,.


6
Votre réponse est une information utile pour une utilisation générale, grepmais cette question porte sur quelque chose de plus subtil et ésotérique. Il semble que vous ayez lu la question trop rapidement pour comprendre le comportement réel interrogé. En outre, GNU grep ne recherche d'arrêt lorsqu'il est utilisé avec -q(comme le permet dans la citation de la spécification POSIX): La page de manuel pour les états de GNU que « Quitter [s] immédiatement à zéro état si un match se trouve » . FWIW, j'ai édité votre question pour montrer comment vous pouvez formater les futurs articles. Bienvenue à Stack Exchange .
Anthony G - justice pour Monica

Cela dit, la réponse de @ user212377 est correcte: dans ce cas, il grepest demandé si «2» existe dans le fichier, rien de plus et rien de moins. Il ne se comporte pas comme sedet consomme les enregistrements jusqu'à ce point et laisse le reste pour un traitement ultérieur. Il lit jusqu'à ce qu'il sache qu'il y a un «2» ou qu'il ne l'est pas, ferme le fichier et renvoie le résultat.
Keith Davies

grepen fait, «consomme tout le fichier» (en ignorant les considérations de mise en mémoire tampon) si la chaîne de recherche n'est pas présente dans le fichier (ce qui n'est prouvable qu'en examinant l'ensemble du fichier). Rien de moins que cela, la lecture du fichier s'arrête , le fichier est fermé et SUCCESS est revenu.
Keith Davies
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.