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
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$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 codeL'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:
^.*?
) 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 (.*?$
).$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.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.
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
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
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.