J'ai cette situation, le fichier contient
abcd line1 line2 line3 vwxyz line4 line5
Je voudrais supprimer chaque fois que je trouve 2 lignes correspondantes de
abcd vwxyz
à être supprimé pour qu'il devienne
abcd line1 line2 line3 vwxyz abcd vwxyz abcd vwxyz abcd vwxyz line4 line5
J'ai recherché de nombreuses suggestions de stackoverflow de correspondance de modèle mais je n'ai jamais rencontré de correspondance de modèle de 2 lignes
J'ai triend sed -i '/ abcd | vwxyz / d' file1
mais cela n'a pas fonctionné comme prévu ...
Toute aide est appréciée en utilisant awk, sed, perl, pyhon etc p>
6 Réponses :
Cela pourrait fonctionner pour vous (GNU sed):
sed -z 's/^abcd\nvwxyz\n//mg' file
Créez une fenêtre de 2 lignes sur toute la longueur du fichier et supprimez cette fenêtre si la fenêtre actuelle correspond aux chaînes requises. Sinon, imprimez / supprimez la première ligne de la fenêtre et ajoutez une autre ligne, répétez.
Ceci peut être étendu pour correspondre à n lignes:
sed ':a;N;s/[^\n]\+/&/3;Ta;/^line1\nline2\nline3$/d;P;D' file
généralisé pour 2 lignes:
Ou:
sed ':a;N;s/[^\n]\+/&/2;Ta;/^abcd\nvwxyz$/d;P;D' file
pour 3 lignes, etc.
Une alternative:
sed 'N;/^abcd\nvwxyz$/d;P;D' file
Merci, la première option semble assez simple ... je dois me cogner la tête pour ne pas avoir pensé à \ n :)
^
signifie "début de chaîne" et non "début de ligne" donc sed -z 's / ^ ...'
ne correspondra pas seulement au texte au début de le fichier? Je n'ai aucune idée de ce que fait le m
avant le g
alors peut-être que c'est pour " m agic" ... :-).
@EdMorton le drapeau m
qui suit la commande de substitution est pour multiligne, où ^
et $
représentent les ancres de début / fin de ligne.
@potong - ah, ok merci pour l'explication. Cette ligne de commande sort juste le fichier d'entrée inchangé quand je l'ai essayé tout à l'heure avec GNU sed 4.4-1.
@EdMorton merci Ed Je suis actuellement sous GNU sed 4.2.2 et je ne rencontre pas ce problème. Veuillez utiliser la première solution et peut-être si cette erreur persiste, envoyez un rapport de bogue au responsable de GNU sed. Je crois que la version actuelle est GNU sed 4.7.
Vous voulez dire quelque chose comme ça?
/abcd/ # search first Pattern { N; # if match read next line /vwxyz/d # if second pattern match then delete line }
explication
sed '/abcd/{N;/vwxyz/d}' yourdata.file
Cela pourrait ne pas supprimer une paire de lignes si une telle paire était précédée de abcd
.
Avec Perl, la solution la plus simple est de charger l'intégralité de l'entrée en mémoire.
if (!eof()) { my @buf = scalar(<>); while (<>) { if ($buf[-1] =~ /^abcd$/ && /^vwxyz$/) { @buf = (); } else { print(shift(@buf)); push(@buf, $_); } } print @buf; }
Si vous lisez une ligne à la fois, la solution générique dans ces types de problèmes est de garder un tampon des dernières lignes.
perl -0777pe's/abcd\nvwxyz\n//g'
Avec awk
, où vous définissez votre séparateur d'enregistrement comme les 2 lignes et vous imprimez simplement chaque ligne.
python3 filter_lines.py abcd line1 line2 line3 vwxyz line4 line5
Une solution python: p>
import re with open('file.in', 'r') as file: data = file.read() print(re.sub(r'(^|\n)abcd\nvwxyz(?=\n)','',data), end='')
sortie:
awk -v RS='abcd\nvwxyz\n' '{printf $0}' file.in abcd line1 line2 line3 vwxyz line4 line5
Vous devez mentionner que nécessite GNU awk pour RS multi-caractères. Utilisez toujours printf "% s", $ 0
au lieu de printf $ 0
car ce dernier échouera lorsque $ 0 contient des caractères de formatage printf tels que % s
. Il devrait vraiment être écrit comme awk -v RS = '(^ | \ n) abcd \ nvwxyz \ n' '1'
mais pour gérer les cas où abcd
et / ou vwxyz
peut apparaître au milieu et / ou au début et / ou à la fin du fichier. J'ai l'impression qu'il manque encore un cas ...
Ah, ouais - le problème avec ce RS dans mon commentaire est qu'il ne correspondra pas au 2ème \ nabcd \ nvwxy \ n
quand il y a 2 blocs consécutifs car il consomme le \ n code > aux deux extrémités de chaque bloc. Je devrais réfléchir à la vraie RS nécessaire pour cela - ce n'est peut-être tout simplement pas une option.
Après réflexion - vous ne pouvez pas faire cela de manière robuste simplement en définissant RS sur la chaîne multiligne cible avec le début / la fin de la ligne environnante, j'ai donc publié une nouvelle réponse.
Pourriez-vous s'il vous plaît essayer ce qui suit, étant donné que votre fichier d'entrée réel est le même que les exemples illustrés.
awk -v RS="" '{gsub(/abcd\nvwxyz/,"");gsub(/[[:space:]]+\n/,ORS)} 1' Input_file
Un peu plus cryptique que je ne le souhaiterais mais avec GNU awk pour RS multi-caractères:
awk -v RS='\nbar\netc\nabcd\nvwxyz\n' -v ORS= '!sub(/(^|\n)foo$/,""){$0=$0 RT} 1' file
Malheureusement, si nous allons avec une solution basée sur RS (par opposition à garder un tampon roulant de 2 lignes ou similaire) alors c'est ainsi que cela doit être fait pour accueillir la chaîne multiligne apparaissant au début et / ou à la fin du fichier et / ou dans des blocs répétés et / ou en commençant / s'arrêtant au milieu d'autres lignes. Il peut être utilisé tel quel pour de longs blocs de lignes arbitraires:
awk -v RS='\nvwxyz\n' -v ORS= '!sub(/(^|\n)foo\nbar\netc\nabcd$/,""){$0=$0 RT} 1' file
ou si vous préférez:
$ awk -v RS='\nvwxyz\n' -v ORS= '!sub(/(^|\n)abcd$/,""){$0=$0 RT} 1' file abcd line1 line2 line3 vwxyz line4 line5
p >
Nous pensions que vous aviez essayé quelque chose ... Code?
sed -i '/ abcd | vwxyz / d' file1 mais cela n'a pas fonctionné comme prévu