web-dev-qa-db-fra.com

Limitez le contexte grep à N caractères en ligne

Je dois parcourir quelques fichiers JSON dans lesquels la longueur des lignes dépasse quelques milliers de caractères. Comment puis-je limiter grep pour afficher le contexte jusqu'à N caractères à gauche et à droite de la correspondance? Tout outil autre que grep conviendrait également, tant qu'il est disponible dans les packages Linux courants.

Ce serait un exemple de sortie, pour le commutateur grep imaginaire Ф:

$ grep -r foo *
hello.txt: Once upon a time a big foo came out of the woods.

$ grep -Ф 10 -r foo *
hello.txt: ime a big foo came of t
36
dotancohen

Avec GNU grep:

N=10; grep -roP ".{0,$N}foo.{0,$N}" .

Explication:

  • -o => N'imprimer que ce que vous avez trouvé
  • -P => Utiliser des expressions régulières de style Perl
  • Le regex dit match 0 à $N caractères suivis de foo suivis de 0 à $N personnages.

Si vous n'avez pas GNU grep:

find . -type f -exec \
    Perl -nle '
        BEGIN{$N=10}
        print if s/^.*?(.{0,$N}foo.{0,$N}).*?$/$ARGV:$1/
    ' {} \;

Explication:

Comme nous ne pouvons plus compter sur grep étant GNU grep, nous utilisons find pour rechercher des fichiers récursivement (le -r action de GNU grep). Pour chaque fichier trouvé, nous exécutons l'extrait de code Perl.

Commutateurs Perl:

  • -n Lire le fichier ligne par ligne
  • -l Supprimer la nouvelle ligne à la fin de chaque ligne et la remettre lors de l'impression
  • -e Traite la chaîne suivante comme du code

L'extrait de code Perl fait essentiellement la même chose que grep. Il commence par définir une variable $N au nombre de caractères de contexte souhaité. Le BEGIN{} signifie que ceci n'est exécuté qu'une seule fois au début de l'exécution, pas une fois pour chaque ligne de chaque fichier.

L'instruction exécutée pour chaque ligne consiste à imprimer la ligne si la substitution d'expression régulière fonctionne.

Le regex:

  • Faites correspondre n'importe quelle vieille chose paresseusement1 en début de ligne (^.*?) suivi par .{0,$N} comme dans le cas grep, suivi de foo suivi d'un autre .{0,$N} et enfin faire correspondre n'importe quelle vieille chose paresseusement jusqu'à la fin de la ligne (.*?$).
  • Nous le remplaçons par $ARGV:$1. $ARGV est une variable magique qui contient le nom du fichier en cours de lecture. $1 correspond à ce que les parens correspondaient: le contexte dans ce cas.
  • Les correspondances paresseuses à chaque extrémité sont requises car une correspondance gourmande mangerait tous les caractères avant foo sans manquer de correspondance (puisque .{0,$N} est autorisé à correspondre à zéro fois).

1Autrement dit, préférez ne rien faire correspondre sauf si cela entraînerait l'échec de la correspondance globale. En bref, faites correspondre le moins de caractères possible.

25
Joseph R.

Essayez d'utiliser celui-ci:

grep -r -E -o ".{0,10}wantedText.{0,10}" *

- E indique que vous souhaitez utiliser l'expression régulière étendue

- o indique que vous souhaitez imprimer uniquement la correspondance

- r grep recherche le résultat récursivement dans le dossier

REGEX:

{0,10} indique le nombre de caractères arbitraires que vous souhaitez imprimer

. représente un caractère arbitraire (un caractère lui-même n'était pas important ici, juste leur nombre)

Edit: Oh, je vois, que Joseph recommande presque la même solution que moi: D

22
Eenoku

Canalisation de la sortie standard vers cut avec le -b drapeau; vous pouvez indiquer à la sortie de grep uniquement les octets 1 à 400 par ligne.

grep "foobar" * | cut -b 1-400
4
Eric Leschinski

Tiré de: http://www.topbug.net/blog/2016/08/18/truncate-long-matching-lines-of-grep-a-solution-that-preserves-color/ et https://stackoverflow.com/a/39029954/1150462

L'approche suggérée ".{0,10}<original pattern>.{0,10}" est parfaitement bon, sauf que la couleur de surbrillance est souvent gâchée. J'ai créé un script avec une sortie similaire mais la couleur est également préservée:

#!/bin/bash

# Usage:
#   grepl PATTERN [FILE]

# how many characters around the searching keyword should be shown?
context_length=10

# What is the length of the control character for the color before and after the matching string?
# This is mostly determined by the environmental variable GREP_COLORS.
control_length_before=$(($(echo a | grep --color=always a | cut -d a -f '1' | wc -c)-1))
control_length_after=$(($(echo a | grep --color=always a | cut -d a -f '2' | wc -c)-1))

grep -E --color=always "$1" $2 | grep --color=none -oE ".{0,$(($control_length_before + $context_length))}$1.{0,$(($control_length_after + $context_length))}"

En supposant que le script est enregistré sous grepl, puis grepl pattern file_with_long_lines devrait afficher les lignes correspondantes mais avec seulement 10 caractères autour de la chaîne correspondante.

2
xuhdev