Je veux supprimer la ligne précédente et la ligne actuelle en fonction de la correspondance du modèle sur la ligne suivante
Ceci est mon sample.txt
sed -Ene ':a;N;/999/{d;}; ba; P' sample.txt
Je veux faire correspondre le modèle 999 et supprimer à la fois lui-même et la ligne précédente
J'essaye cette commande mais je n'obtiens aucun résultat
This is test line 11 This is test line 999 This is test line 12 This is test line 13 This is test line 16 This is test line 999 This is test line 17 This is test line 18
5 Réponses :
Pourriez-vous s'il vous plaît essayer de suivre (si vous êtes d'accord avec awk
).
awk 'prev && $NF!=999{print prev ORS FNR,$0;prev="";next} $NF==999{prev=""} $NF!=999{prev=FNR FS $0} END{if(prev){print prev}}' Input_file
Ou si vous avez un nombre pair de lignes et que vous voulez vous occuper de l'impression en dernier impair pair.
awk 'prev && $NF!=999{print prev ORS FNR,$0;prev="";next} $NF==999{prev=""} $NF!=999{prev=FNR FS $0}' Input_file
Je veux utiliser sed uniquement
Pour une solution sed 1 , qui gère tous les cas de bord (999 dans les 2 premières lignes ou les lignes consécutives de 999):
This is test line 12 This is test line 13 This is test line 17 This is test line 18
Résultat: p >
sed ' 1{ /999/d # Special case needed for line 1. Delete if it contains 999. } $!N # Append next line. $!N stops exit w/o printing at EOF. /999/d # If pattern space contains 999, d & begin next cycle. P # If we get to here, there is no 999. Print to first newline. D # Delete to first newline. ' FILE
1 Testé à la fois sous BSD (Mac OS X) et GNU sed.
Merci pour cela, pouvez-vous s'il vous plaît me dire où je me trompe avec mon sed si j'ai besoin d'utiliser le contrôle de flux
@rgd, votre script est : a; N; / 999 / d; ba; P
. N
ajoute la ligne suivante à l'espace de motif. Ensuite, si / 999 /
d
supprime complètement l'espace de motif et commence le cycle suivant. Sinon / 999 /
, allez quand même à a
. Ainsi, votre commande P
n'est pas accessible, c'est pourquoi vous n'obtenez aucune sortie.
Merci, comment puis-je corriger mon code, je veux dire où dans le code je dois mettre P
?
Cela pourrait fonctionner pour vous (GNU sed):
sed -n ':a;$!N;/\n.*999/{:b;n;/999/bb;ba};/999/!P;D' file
Ouvrez une fenêtre en cours d'exécution de deux lignes sur toute la longueur du fichier.
Si la deuxième ligne de la fenêtre contient 999
supprimer les deux lignes.
Sinon, imprimer la première ligne de la fenêtre, supprimer la première ligne et répéter.
Une solution alternative pour ligne 1 ou 2 lignes contiguës ou plus contenant 999
:
sed 'N;/\n.*999/d;P;D' file
Celui-ci ne fonctionnera pas si 999 est dans la première ligne. Je l'ai résolu en utilisant un cas spécial pour la ligne 1. Je ne sais pas s'il existe un meilleur moyen.
@AlexHarvey l'OP n'a pas précisé ce qui devrait se passer si la première ligne contient 999
ni ce qui devrait se passer lorsque 2 lignes ou plus contiennent 999
. Cependant voir modifier.
Je pense que \ n. * N'est pas nécessaire dans le premier regex?
@AlexHarvey je pense que c'est pour le cas de lire 999 en deuxième ligne seulement. Quelques questions 1. quand nous passons de l'étiquette B à A, la ligne suivante à lire est-elle la ligne que nous avons initialement laissée ou commence-t-elle à partir de la ligne où l'étiquette intérieure B se termine? Je veux savoir quand n
lit une ligne continue contenant 999 et puis quand elle revient à l'étiquette A, alors quel sera le contenu de N
Il se branche immédiatement et sans condition comme une déclaration goto
@AlexHarvey j'ai posé une autre question pour dissiper mon doute stackoverflow.com/questions/55822978/...
Pour ce que ça vaut, j'ai écrit des tests unitaires pour tester mon code et celui de Potong. J'ai convenu que \ n. *
est nécessaire, mais c'est lorsque 999 est sur la première ligne. Ma solution ci-dessous gère tous les cas marginaux, j'ai donc au moins raison de dire que le branchement n'est pas réellement nécessaire. Consultez mon code de test unitaire ici .
essayé sur gnu sed
sed -Ez 's/[^\n]*\n[^\n]*999\n//g' sample.txt
Avec un exemple d'entrée plus complet de:
$ awk '$NF==999{p="";next} {printf "%s",p; p=$0 ORS} END{printf "%s",p}' file This is test line 999 This is test line 11 This is test line 999 This is test line 12 This is test line 13 This is test line 999 This is test line 999 This is test line 999 This is test line 14 This is test line 15 This is test line 16 This is test line 999 This is test line 17 This is test line 18
Essayez ceci:
$ cat tst.awk $NF == 999 { prev = "" next } { printf "%s", prev prev = $0 ORS } END { printf "%s", prev } $ awk -f tst.awk file This is test line 12 This is test line 14 This is test line 15 This is test line 17 This is test line 18
ou si vous préférez la brièveté à la clarté: p>
$ cat file This is test line 999 This is test line 11 This is test line 999 This is test line 12 This is test line 13 This is test line 999 This is test line 999 This is test line 999 This is test line 14 This is test line 15 This is test line 16 This is test line 999 This is test line 17 This is test line 18
Notez que ce qui précède fonctionnera même si une autre partie de votre ligne que le dernier champ contenait 999
ou si le dernier champ était 9999
au lieu de votre cible 999
, il n'est pas nécessaire que 999
soit écrit / testé plusieurs fois dans le script, si vous vouliez tester, par exemple, le 3e champ de la ligne au lieu du dernier champ, vous pouvez simplement changer $ NF
en $ 3
=, si vous VOULEZ tester toute la ligne pour une expression rationnelle, vous venez de changer $ NF == 999
à / 999 /
, cela fonctionnera même si votre chaîne cible contient des métacaractères d'expression régulière, et cela fonctionnera dans n'importe quel awk dans n'importe quel shell sur n'importe quel UNIX boîte.
Ce n'est pas beaucoup plus simple que ma solution sed, n'est-ce pas? Je prends le point de la portabilité. Sinon, au lieu du magique N
qui s'ajoute à l'espace de motif, vous avez un prev = $ 0 ORS
magique qui fait la même chose que le N
de sed.
Je n'ai pas eu à ajouter de commentaires pour que mon code soit clairement compréhensible et il n'y a rien de magique dans une affectation de variable. De plus, contrairement à seds N
, si nous voulions améliorer pour supprimer, disons, 10 lignes avant la ligne cible au lieu de 1, je n'aurais pas à écrire cette déclaration 10 fois pour enregistrer 10 lignes. d juste faire prev un tableau de 10 lignes avec une boucle pour l'imprimer et si l'OP voulait pouvoir utiliser un nombre variable de lignes spécifié au moment de l'exécution c'est exactement la même solution dans awk que pour une valeur statique mais impossible à faire dans 1 script sed.
Le fait est qu'il y a des avantages au script awk et aucun avantage au script sed. Avec awk: besoin d'imprimer un nombre de lignes supprimées stderr? banal. Besoin de tester 999 dans un domaine spécifique? banal. Besoin de tester une chaîne d'entrée (999) comme littérale quand elle contient des métachars d'expression régulière? Banal. Besoin de supprimer également les lignes APRÈS la ligne cible? Banal. Besoin de supprimer uniquement les lignes précédentes si elles contiennent une autre chaîne? Banal. etc., etc. Avec awk si / quand les exigences changent, vous vous contentez généralement de construire sur ce que vous avez déjà alors qu'avec sed c'est généralement soit impossible, soit une réécriture
Oh allez, awk est juste python ou C ou tout autre langage basé sur un algorithme avec moins de constructions de langage, une demi-douzaine de mots-clés, une boucle implicite while read split
et implicite if
s autour des blocs condition {action}
. sed d'un autre côté est très différent à la fois dans la syntaxe et la sémantique. la maintenabilité d'un script de 5 lignes - vous n'écrivez pas simplement un script d'une certaine manière en vue d'améliorer CE script, vous l'écrivez de cette façon afin que lorsque la prochaine tâche similaire arrive, vous puissiez écrire ce script suivant essentiellement de la même manière mais avec de petites différences si nécessaire.
L'écriture de scripts maintenables ne devrait pas être quelque chose que vous décidez de faire lorsque cela est nécessaire, cela devrait être ce que vous faites à moins qu'il n'y ait un besoin pressant de ne pas le faire, par exemple. pour des raisons de performances. Quoi qu'il en soit, comme je l'ai dit dans un autre fil de discussion, il m'a fallu 10 ans d'utilisation de sed (et shell) pour manipuler du texte avant de décider de commencer à utiliser awk pour des choses qui étaient plus que simplement s / old / new /
et on dirait que vous êtes juste au début de votre propre voyage avec cela et heureux de le continuer ainsi - tout le meilleur.
Peut-être existe-t-il une approche encore meilleure que de manipuler du texte compliqué à partir de vim? Dans tous les cas, heureux que vous ayez une approche avec laquelle vous êtes satisfait - bonne chance.
sed est le meilleur outil pour faire
s / old / new /
sur des chaînes individuelles. Ce n'est pas ce que vous faites ici, donc sed ne serait pas le meilleur outil pour le travail, alors pourquoi voulez-vous utiliser sed pour cela quand cela peut être fait plus clair, plus simple, plus robuste, plus portable et / ou plus efficace avec un autre outil? Vous devez inclure un cas où999
apparaît comme la première ligne de votre entrée et où il apparaît sur 3 lignes contiguës pour montrer comment vous voulez que ceux-ci soient traités et nous donner quelque chose pour tester de manière adéquate une solution potentielle.Je pense que c'était une excellente question @rgd.