Limiter la sortie grep aux lignes courtes

8

J'utilise souvent grep pour trouver des fichiers ayant une certaine entrée comme celle-ci:

grep -R 'MyClassName'

La bonne chose est qu'il retourne les fichiers, leur contenu et marque la chaîne trouvée en rouge. La mauvaise chose est que j'ai également des fichiers énormes où le texte entier est écrit sur une seule grande ligne. Maintenant, grep génère trop de résultats lors de la recherche de texte dans ces gros fichiers. Existe-t-il un moyen de limiter la sortie à par exemple 5 mots à gauche et à droite? Ou peut-être limiter la sortie à 30 lettres à gauche et à droite?

Socrate
la source
3
cut
Diffusez
Supposons que le motif que vous recherchez se trouve à la position 50, mais vous avez dit que vous ne vouliez que 30 lettres, que voulez-vous faire alors? Ignorer cette ligne ou l'inclure également dans la sortie mais la couper? Que voulez-vous exactement limiter - la recherche ou les lignes elles-mêmes?
Sergiy Kolodyazhnyy
1
@Rinzwind Je ne comprends pas très bien ce que vous voulez réaliser cut, car il se divise uniquement par délimiteur ou par nombre de caractères. Mais quand je trouve une ligne avec MyClassNameelle, elle peut être n'importe où sur la ligne et pas toujours à la même position. De plus, il peut y avoir une variation de caractères à l'avant et à l'arrière de celui-ci, ce qui rompt la possibilité de diviser par délimiteur.
Socrates
1
@SergiyKolodyazhnyy Lorsqu'une ligne positive avec MyClassNamea été trouvée, je souhaite obtenir le nom du fichier et les caractères x à gauche et à droite. x est n'importe quel nombre que je fournis, par exemple 30. Le reste du contenu du fichier doit être ignoré. Il s'agit d'obtenir un contexte pour les fichiers correspondants et de limiter la surcharge.
Socrates
1
@Rinzwind Avec quel type de délimiteur personnalisé suggéreriez-vous cuts'il y a trois fichiers avec l'entrée suivante: oiadfaosuoianavMyClassNameionaernaldfajdet /(/&%%§%/(§(/MyClassName&((/$/$/(§/$&et public class MyClassName { public static void main(String[] args) { } }?
Socrates

Réponses:

15

greplui-même n'a que des options de contexte basées sur des lignes. Une alternative est suggérée par ce post SU :

Une solution de contournement consiste à activer l'option 'correspondance uniquement' puis à utiliser la puissance de RegExp pour grep un peu plus que votre texte:

grep -o ".\{0,50\}WHAT_I_M_SEARCHING.\{0,50\}" ./filepath

Bien sûr, si vous utilisez la surbrillance des couleurs, vous pouvez toujours grep à nouveau pour ne colorer que la correspondance réelle:

grep -o ".\{0,50\}WHAT_I_M_SEARCHING.\{0,50\}"  ./filepath | grep "WHAT_I_M_SEARCHING"

Comme autre alternative, je suggère de foldsaisir le texte, puis de le saluer, par exemple:

fold -sw 80 input.txt | grep ...

L' -soption fera foldpousser les mots vers la ligne suivante au lieu de les interrompre.

Ou utilisez une autre méthode pour diviser l'entrée en lignes en fonction de la structure de votre entrée. (Le post SU, par exemple, traitait de JSON, donc utiliser jqetc. pour joli imprimer et grep... ou simplement utiliser jqpour faire le filtrage par lui-même ... serait mieux que l'une des deux alternatives données ci-dessus.)


Cette méthode awk GNU pourrait être plus rapide:

gawk -v n=50 -v RS='MyClassName' '
  FNR > 1 { printf "%s: %s\n",FILENAME, p prt substr($0, 0, n)}
  {p = substr($0, length - n); prt = RT}
' input.txt
  • Dites à awk de diviser les enregistrements sur le motif qui nous intéresse ( -v RS=...) et le nombre de caractères dans context ( -v n=...)
  • Chaque enregistrement après le premier enregistrement ( FNR > 1) est celui où awk a trouvé une correspondance pour le motif.
  • Nous imprimons donc nles derniers caractères de la ligne précédente ( p) et les npremiers caractères de la ligne actuelle ( substr($0, 0, n)), ainsi que le texte correspondant à la ligne précédente (qui est prt)
    • nous définissons pet prt après l' impression, la valeur que nous définissons est utilisée par la ligne suivante
    • RT est un GNUisme, c'est pourquoi il est spécifique à GNU awk.

Pour une recherche récursive, peut-être:

find . -type f -exec gawk -v n=50 -v RS='MyClassName' 'FNR>1{printf "%s: %s\n",FILENAME, p prt substr($0, 0, n)} {p = substr($0, length-n); prt = RT}' {} +
muru
la source
2
Ok, ça marche. Semble Regex est une approche valide, alors merci pour cela. Le temps de traitement est cependant assez grand. Sans Regex comme dans mon article ci-dessus, cela prend 4,912s et avec Regex comme dans votre article, cela prend 3m39.312s.
Socrates
1
@Socrates voit si la méthode awk que j'ai ajoutée ci-dessus fonctionne mieux
muru
1
La foldméthode ne peut être utilisée que si vous êtes sûr que la chaîne recherchée n'apparaît pas à la bordure, sinon elle serait masquée par grep.
Melebius
1
@muru Merci pour votre suggestion avec gawk. Malheureusement, la commande suggérée avec des findsorties aléatoires et aucun nom de fichier, lorsqu'elle est exécutée sur mon système. De plus, je ne parle pas assez awkbien pour analyser correctement la commande. Actuellement, Regex en combinaison avec greprésout le problème peut-être pas rapide, mais fiable. Encore merci.
Socrates
1
@Socrates Je pense que j'ai réussi à corriger la commande awk. Mon modèle mental était erroné sur la ligne RTet le préfixe, etc. à utiliser.
muru
1

L'utilisation de l'appariement uniquement en combinaison avec d'autres options (voir ci-dessous), pourrait être très proche de ce que vous recherchez, sans la surcharge de traitement de l'expression régulière mentionnée dans l'autre réponse.

grep -RnHo 'MyClassName'
  • n sortie numérique, affiche le numéro de ligne de la correspondance
  • H nom de fichier, affiche le nom de fichier au début de la ligne de la correspondance
  • o correspond uniquement, n'affiche que la chaîne mathématique, pas toute la ligne
Robert Riedl
la source
S'il est vrai que le résultat est trouvé beaucoup plus rapidement, il manque des informations. Le chemin du fichier est affiché, le numéro de ligne est affiché, mais la sortie de texte n'est que ma recherche initiale MyClassName. Par conséquent, le contexte manque.
Socrates
grep -RnHo "MyClassName"et grep -Rno "MyClassName"ont la même sortie.
Socrates
La sortie @Socrates n'est pas la même sans H dans le même répertoire
Robert Riedl
Le -odrapeau pourrait être intéressant si le regex avait une partie variable. Pour une chaîne fixe, il est inutile de l'imprimer à chaque fois. OP est très probablement intéressé par le contexte proche.
Melebius
1
@Socrates, vrai - le contexte manque, mais je pensais que c'était le but? Limiter la sortie? Vous pouvez à nouveau ajouter du contexte en ajoutant les lignes avant ( -B 1) ou après ( -A 1). Désolé de ne pas avoir pu vous aider davantage.
Robert Riedl