Il semble que jutilise mal grep
/ egrep
.
Jessayais de rechercher des chaînes sur plusieurs lignes et je nai pas trouvé de correspondance alors que je sais que ce que je recherche doit correspondre. Au départ, je pensais que mes expressions régulières étaient fausses mais jai finalement lu que celles-ci les outils fonctionnent par ligne (mes expressions régulières étaient aussi si triviales que cela ne pouvait pas être le problème).
Alors, quel outil utiliserait-on pour rechercher des modèles sur plusieurs lignes?
Commentaires
Réponse
Voici « sa sed
celui qui vous donnera un comportement semblable à grep
sur plusieurs lignes:
sed -n "/foo/{:start /bar/!{N;b start};/your_regex/p}" your_file
Comment ça marche
-
-n
supprime le comportement par défaut dimpression de chaque ligne -
/foo/{}
lui indique de correspondre àfoo
et faites ce qui se trouve à lintérieur des squigglies jusquaux lignes correspondantes. Remplacezfoo
par la partie de départ du motif. -
:start
est une étiquette de branchement pour nous aider à continuer à boucler jusquà ce que nous trouvions la fin de notre regex. -
/bar/!{}
exécutera ce qui est dans les squigglies à les lignes qui ne correspondent pas àbar
. Remplacez avec la partie finale du motif. -
N
ajoute la ligne suivante au tampon actif (sed
appelle cela lespace de motif) -
b start
se branchera inconditionnellement à létiquettestart
que nous avons créée plus tôt afin de continuer à ajouter la ligne suivante tant que lespace du motif ne contient pasbar
. -
/your_regex/p
imprime lespace du motif sil correspond àyour_regex
. Vous devez remplaceryour_regex
par lexpression entière que vous souhaitez faire correspondre sur plusieurs lignes.
Commentaires
- +1 Ajout de ceci à la toolikt! Merci.
- Remarque: sur MacOS, cela génère une erreur
sed: 1: "/foo/{:start /bar/!{N;b ...": unexpected EOF (pending }'s)
- Obtention de
sed: unterminated {
- @Nomaed Tourné dans le noir ici, mais votre expression régulière contient-elle des caractères » {« ? Si tel est le cas, vous ‘ devrez les échapper à la barre oblique inverse.
- @Nomaed Il semble que cela ait à voir avec les différences entre les implémentations de
sed
. Jai essayé de suivre les recommandations de cette réponse pour rendre le script ci-dessus conforme à la norme, mais cela ma dit que » start » était un étiqueter. Donc, je ‘ ne sais pas si cela peut être fait dune manière conforme aux normes. Si vous le gérez, nhésitez pas à modifier ma réponse.
Réponse
Jutilise généralement un outil appelé pcregrep
qui peut être installé dans la plupart des versions Linux en utilisant yum
ou apt
.
Par exemple.
Supposons que vous ayez un fichier nommé testfile
avec un contenu
abc blah blah blah def blah blah blah
Vous pouvez exécuter la commande suivante:
$ pcregrep -M "abc.*(\n|.)*def" testfile
pour faire une correspondance de motifs sur plusieurs lignes.
De plus, vous pouvez faire la même chose avec sed
.
$ sed -e "/abc/,/def/!d" testfile
Commentaires
- cette
sed
suggestion est ignorée la ligne oùdef
se trouverait
Answer
Simplement un grep normal qui prend en charge le Perl-regexp
paramètre P
fera ce travail.
$ echo "abc blah blah blah def blah blah blah" | grep -oPz "(?s)abc.*?def" abc blah blah blah def
(?s)
appelé le modificateur DOTALL qui fait que le point dans votre expression régulière correspond non seulement aux caractères mais aussi aux sauts de ligne.
Commentaires
- Quand jessaye cette solution, la sortie ne se termine pas à ‘ def ‘ mais va à la fin du fichier ‘ blah ‘
- peut-être votre grep ne prend pas en charge loption
-P
- Cétait la seule qui fonctionnait pour moi – jai essayé toutes les
sed
suggestions, mais ‘ nest pas allé jusquà installer des alternatives grep. -
$ grep --version
:grep (GNU grep) 3.1
dans Windows Git Bash a une option-P, --perl-regexp
mais(?s)
ne ‘ t semble fonctionner là-bas. Il affiche toujours la première ligne uniquement. Le même modèle avec la même chaîne de test fonctionne sur regex101.com . Y a-t-il une alternative dans le Git Bash?sed
? (sed (GNU sed) 4.8
ici) - Savez-vous comment ajouter un contexte à la sortie? grep -1 ne fonctionne ‘ ici.
Réponse
Voici « une approche plus simple utilisant Perl:
perl -e "$f=join("",<>); print $& if $f=~/foo\nbar.*\n/m" file
ou (puisque JosephR a pris le sed
itinéraire , je « volerai sans vergogne sa suggestion )
perl -n000e "print $& while /^foo.*\nbar.*\n/mg" file
### Explication
$f=join("",<>);
: ceci lit le fichier entier et enregistre son contenu (retours à la ligne et tout) dans la variable $f
. Nous essayons ensuite de faire correspondre foo\nbar.*\n
, et de limprimer si cela correspond (la variable spéciale $&
contient la dernière correspondance trouvée). ///m
est nécessaire pour faire correspondre lexpression régulière entre les sauts de ligne.
-0
définit le séparateur denregistrement dentrée. Le paramétrer sur 00
active le « mode paragraphe » où Perl utilisera des retours à la ligne consécutifs (\n\n
) comme séparateur denregistrement. Dans les cas où il ny a pas de sauts de ligne consécutifs, le fichier entier est lu (slurped) à la fois.
### Attention: Ne faites pas cela pour les gros fichiers, il se chargera le fichier entier en mémoire et cela peut être un problème.
Commentaires
- I don ‘ t en savoir beaucoup sur Perl, mais ne ‘ t-il pas besoin dêtre
my $f=join("",<>);
à proprement parler? - @Sapphire_Brick uniquement si vous êtes en mode strict (
use strict;
). C’est ‘ une bonne habitude à prendre, en particulier lors de l’écriture de scripts plus volumineux, mais ‘ est un problème pour un petit one-liner comme celui-ci un.
Réponse
Supposons que nous ayons le fichier test.txt contenant:
blabla blabla foo here is the text to keep between the 2 patterns bar blabla blabla
Le code suivant peut être utilisé:
sed -n "/foo/,/bar/p" test.txt
Pour la sortie suivante:
foo here is the text to keep between the 2 patterns bar
Réponse
Lalternative grep sift prend en charge la correspondance multiligne (avertissement: je suis lauteur).
Supposons que testfile
contient:
<book> <title>Lorem Ipsum</title> <description>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua</description> </book>
sift -m '<description>.*?</description>'
(affiche les lignes contenant la description)
Résultat:
testfile: <description>Lorem ipsum dolor sit amet, consectetur testfile: adipiscing elit, sed do eiusmod tempor incididunt ut testfile: labore et dolore magna aliqua</description>
sift -m '<description>(.*?)</description>' --replace 'description="$1"' --no-filename
(extraire et reformater la description)
Résultat:
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua"
Commentaires
- Très bel outil. Toutes nos félicitations! Essayez de linclure dans des distributions comme Ubuntu.
Réponse
Jai résolu celui-ci pour moi en utilisant grep et – Une option avec un autre grep.
grep first_line_word -A 1 testfile | grep second_line_word
Loption -A 1 imprime 1 ligne après la ligne trouvée. Bien sûr, cela dépend de votre combinaison de fichiers et de mots. Mais pour moi, cétait la solution la plus rapide et la plus fiable.
Commentaires
- alias grepp = ‘ grep –color = auto -B10 -A20 -i ‘ puis cat somefile | grepp blah | grepp foo | grepp bar … oui ceux -A et -B sont très pratiques …vous avez la meilleure réponse
- Ceci nest pas ‘ t super déterministe et il ignore tout le modèle en faveur de lobtention dune seule ligne différente (juste en fonction de sa proximité à la première ligne). Il est ‘ de dire au programme d’aller aussi loin que nécessaire pour arriver à une sorte de modèle que vous ‘ re absolument certain que la fin du texte que vous ‘ essayez de faire correspondre. Par exemple, si
testfile
est mis à jour de telle sorte quesecond_line_word
se trouve sur la troisième ligne, non seulement vous manquez maintenant la première ligne (en raison de votre deuxièmegrep
) mais vous ‘ ne manquez pas la ligne qui a commencé à apparaître entre les deux. - Ceci serait un MO assez bon pour les commandes ad hoc où vous ne voulez vraiment quune seule ligne en sortie que vous avez déjà comprise. Je ne ‘ ne pense pas que ‘ est ce que lOP est après et vous pourriez probablement aussi simplement copier / coller à ce stade en raison de cela étant ad hoc.
Réponse
Une façon de faire est dutiliser Perl. par exemple. voici le contenu dun fichier nommé foo
:
foo line 1 bar line 2 foo foo foo line 5 foo bar line 6
Maintenant, voici un peu de Perl qui va correspond à toute ligne commençant par foo suivie de toute ligne commençant par bar:
cat foo | perl -e "while(<>){$all .= $_} while($all =~ /^(foo[^\n]*\nbar[^\n]*\n)/m) { print $1; $all =~ s/^(foo[^\n]*\nbar[^\n]*\n)//m; }"
Le Perl, décomposé:
-
while(<>){$all .= $_}
Ceci charge toute lentrée standard dans la variable$all
-
while($all =~
Alors que la variableall
a lexpression régulière … -
/^(foo[^\n]*\nbar[^\n]*\n)/m
Le regex: foo au début de la ligne, suivi par un nombre quelconque de caractères non-nouvelle ligne, suivi dun saut de ligne, suivi immédiatement par « bar », et le reste de la ligne avec bar./m
à la fin de lexpression régulière signifie « correspondance sur plusieurs lignes » -
print $1
Imprimer la partie de lexpression régulière qui était entre parenthèses (dans ce cas, lexpression régulière entière) -
s/^(foo[^\n]*\nbar[^\n]*\n)//m
Effacez la première correspondance pour lexpression régulière, afin que nous puissions faire correspondre plusieurs cas de lexpression régulière dans le fichier en question
Et la sortie:
foo line 1 bar line 2 foo bar line 6
Commentaires
- Je viens de dire que votre Perl peut être raccourci au plus idiomatique:
perl -n0777E 'say $& while /^foo.*\nbar.*\n/mg' foo
Réponse
Si nous voulons obtenir le texte entre les 2 motifs en excluant eux-mêmes.
Supposons que nous ayons le fichier test.txt contenant:
blabla blabla foo here is the text to keep between the 2 patterns bar blabla blabla
Le code suivant peut être utilisé:
sed -n "/foo/{ n b gotoloop :loop N :gotoloop /bar/!{ h b loop } /bar/{ g p } }" test.txt
Pour la sortie suivante:
here is the text to keep between the 2 patterns
Comment ça marche, laissez « s faites-le étape par étape
-
/foo/{
est déclenché lorsque la ligne contient « foo » -
n
remplacez lespace du motif par la ligne suivante, cest-à-dire le mot « here » -
b gotoloop
branche sur létiquette « gotoloop » -
:gotoloop
définit le libellé « gotoloop » -
/bar/!{
si le motif ne contient pas « bar » -
h
remplacer lespace de maintien par un motif, donc « here » est enregistré dans lespace de maintien -
b loop
branche au libellé « loop » -
:loop
définit le libellé « loop » -
N
ajoute le modèle à lespace de conservation.
Lespace de conservation contient maintenant:
« ici »
« est le » -
:gotoloop
Nous sommes maintenant à létape 4, et boucle jusquà ce quune ligne contienne « bar » -
/bar/
la boucle est terminée, « bar » a été trouvé, il » s lespace du motif - lespace de motif est remplacé par un espace de maintien qui contient toutes les lignes entre » foo « et » bar « qui ont été sauvegardées pendant la boucle principale
-
p
copie lespace de motif sur la sortie standard
Terminé!
Commentaires
- Bravo, +1. Jévite généralement dutiliser ces commandes en tr ‘ les nouvelles lignes dans SOH et en exécutant des commandes sed normales puis en remplaçant les nouvelles lignes.
grep
. Ils sont étroitement liés mais pas dups, IMO."grep"
suggérant le verbe » à grep « , et les principales réponses, y compris accepté, nutilisez pas ‘ grep.