Don peut être mieux dans la plupart des cas, mais juste au cas où le fichier est vraiment gros et que vous ne pouvez pas sed
gérer un fichier de script aussi volumineux (ce qui peut arriver à environ 5000+ lignes de script) , le voici avec plain sed
:
sed -ne:t -e"/\n.*$match/D" \
-e'$!N;//D;/'"$match/{" \
-e"s/\n/&/$A;t" \
-e'$q;bt' -e\} \
-e's/\n/&/'"$B;tP" \
-e'$!bt' -e:P -e'P;D'
Ceci est un exemple de ce qu'on appelle une fenêtre coulissante en entrée. Il fonctionne en construisant une anticipation tampon$B
lignes -count avant même d' essayer d'imprimer quoi que ce soit.
Et en fait, je devrais probablement clarifier mon point précédent: le limiteur de performance principal pour cette solution et pour Don sera directement lié à l'intervalle. Cette solution va ralentir avec un intervalle de plus grandes tailles , alors que don de ralentiront avec un intervalle de plus grandes fréquences . En d'autres termes, même si le fichier d'entrée est très volumineux, si l'occurrence réelle de l'intervalle est encore très peu fréquente, sa solution est probablement la voie à suivre. Cependant, si la taille de l'intervalle est relativement gérable et est susceptible de se produire souvent, c'est la solution que vous devez choisir.
Voici donc le workflow:
- Si
$match
se trouve dans l'espace de motif précédé d'une ligne \n
électronique, supprimera sed
récursivement D
chaque ligne \n
électronique qui la précède.
- J'éclaircissais
$match
train de vider complètement l'espace des motifs avant - mais pour gérer facilement les chevauchements, laisser un point de repère semble fonctionner beaucoup mieux.
- J'ai également essayé
s/.*\n.*\($match\)/\1/
d'essayer de l'obtenir en une seule fois et d'esquiver la boucle, mais lorsqu'elle $A/$B
est grande, la D
boucle elete se révèle considérablement plus rapide.
- Ensuite, nous tirons sur la
N
ligne d'entrée ext précédée d'un \n
délimiteur ewline et essayons une fois de plus d' D
éliminer un /\n.*$match/
en se référant à notre expression régulière w / la plus récemment utilisée //
.
- Si l'espace de motif correspond,
$match
il ne peut le faire qu'avec $match
en tête de ligne - toutes les $B
lignes précédentes ont été effacées.
- Nous commençons donc en boucle sur
$A
fter.
- Chaque terme de cette boucle , nous allons tenter de
s///
ubstitute pour &
lui - même le $A
e \n
caractère ewline dans l' espace de modèle, et, en cas de succès, l' t
Est nous branche - et tout notre $A
tampon de près avoir - sur le script entièrement pour lancer le script au- dessus de la partie supérieure avec la ligne d'entrée suivante, le cas échéant.
- Si l'
t
est ne réussit pas, nous allons b
ranch vers l' :t
étiquette d'opération et recurse pour une autre ligne d'entrée - éventuellement recommencer la boucle si $match
se produit lors de la collecte $A
fter.
- Si nous obtenons passé une
$match
boucle de fonction, nous allons essayer de p
Rint la $
dernière ligne si cela est, et si !
ne pas essayer de s///
ubstitute pour &
lui - même le $B
e \n
caractère ewline dans l' espace modèle.
- Nous l'
t
estimerons également, et si cela réussit, nous passerons à l' :P
étiquette rint.
- Si ce n'est pas le cas, nous allons revenir à
:t
op et obtenir une autre ligne d'entrée ajoutée au tampon.
- Si nous faisons à
:P
Rint nous P
Rint alors D
élete jusqu'à la première \n
ewline dans l' espace de configuration et exécutez à nouveau le script du haut avec ce qui reste.
Et donc cette fois, si nous faisions A=2 B=2 match=5; seq 5 | sed...
L'espace de motif pour la première itération à :P
rint ressemblerait à:
^1\n2\n3$
Et c'est ainsi que sed
rassemble son $B
tampon efore. Et donc sed
imprime sur les $B
lignes de sortie -comptes derrière l'entrée qu'il a recueillies. Cela signifie que, compte tenu de notre exemple précédent, sed
se P
Rint 1
à la sortie, puis D
elete et que renvoyer au début du script un espace de motif qui ressemble à :
^2\n3$
... et en haut du script, la N
ligne d'entrée ext est récupérée et donc l'itération suivante ressemble à:
^2\n3\n4$
Et donc quand nous trouvons la première occurrence de 5
in input, l'espace de motif ressemble en fait à:
^3\n4\n5$
Ensuite, la D
boucle elete entre en jeu et quand elle est terminée, elle ressemble à:
^5$
Et lorsque la N
ligne d'entrée ext est tirée, sed
frappe EOF et quitte. À ce moment-là, il n'a encore P
imprimé que les lignes 1 et 2.
Voici un exemple d'exécution:
A=8 B=7 match='[24689]0'
seq 100 |
sed -ne:t -e"/\n.*$match/D" \
-e'$!N;//D;/'"$match/{" \
-e"s/\n/&/$A;t" \
-e'$q;bt' -e\} \
-e's/\n/&/'"$B;tP" \
-e'$!bt' -e:P -e'P;D'
Cela imprime:
1
2
3
4
5
6
7
8
9
10
11
12
29
30
31
32
49
50
51
52
69
70
71
72
99
100
$A
et / ou$B
. Plus vous augmentez ces chiffres, plus ils seront lents - mais vous pouvez les rendre raisonnablement grands.Vous pouvez utiliser
gnu grep
avec-A
et-B
pour imprimer exactement les parties du fichier que vous souhaitez exclure, mais ajoutez le-n
commutateur pour imprimer également les numéros de ligne, puis formatez la sortie et passez-la en tant que script de commandesed
pour supprimer ces lignes:Cela devrait également fonctionner avec des fichiers de modèles transmis
grep
via-f
par exemple:Je pense que cela pourrait être légèrement optimisé s'il effondrait trois numéros de ligne consécutifs ou plus dans des plages de manière à avoir, par exemple,
2,6d
au lieu de2d;3d;4d;5d;6d
... bien que si l'entrée n'a que quelques correspondances, cela ne vaut pas la peine de le faire.Autres moyens qui ne préservent pas l'ordre des lignes et sont probablement plus lents:
avec
comm
:comm
nécessite l' intervention Sorted ce qui signifie que l'ordre de ligne ne serait pas conservée dans la sortie finale ( à moins que votre fichier est déjà triée) doncnl
est utilisé pour numéroter les lignes avant de tri,comm -13
imprime des lignes uniques au 2ème FICHIER puiscut
supprime la partie qui a été ajoutée parnl
(c'est-à-dire le premier champ et le délimiteur:
)avec
join
:la source
comm
soit plus rapide que l'original avecsed
etgrep
?Si cela ne vous dérange pas d'utiliser
vim
:-Nes
active le mode ex silencieux non compatible. Utile pour l'écriture de scripts.+{command}
dites à vim de s'exécuter{command}
sur le fichier.g/${PAT}/
- sur toutes les lignes correspondantes/fff/
. Cela devient difficile si le modèle contient des caractères spéciaux d'expression régulière que vous n'aviez pas l'intention de traiter de cette façon..-${B}
- à partir d'une ligne au-dessus de celle-ci.+${A}
- à 2 lignes en dessous de celle-ci (voir:he cmdline-ranges
pour ces deux)d
- supprimez les lignes.+w !tee
écrit ensuite sur la sortie standard.+q!
se ferme sans enregistrer les modifications.Vous pouvez ignorer les variables et utiliser directement le modèle et les nombres. Je les ai utilisés juste pour des raisons de clarté.
la source
Que diriez-vous (en utilisant GNU
grep
etbash
):Ici, nous trouvons les lignes devant être supprimées
grep -B2 -A1 'fff' file.txt
, puis en l'utilisant comme fichier d'entrée pour trouver les lignes souhaitées les rejetant.la source
kos
la solution (maintenant supprimée) comme s'il y avait des lignes en double dans le fichier d'entrée et que certaines d'entre elles se trouvaient en dehors de la plage et que d'autres se trouvaient dans cette plage, cela les supprimerait toutes. En outre, avec plusieurs occurrences de modèle , s'il y a des lignes comme--
dans le fichier d'entrée (en dehors des plages), cela les supprimera car le délimiteur--
apparaît dansgrep
la sortie de 'lorsque plus d'une ligne correspond au modèle (ce dernier est hautement improbable mais vaut la peine). mentionnant je suppose).Vous pouvez obtenir un résultat satisfaisant en utilisant des fichiers temporaires:
Le résultat est assez bon car vous pouvez perdre une indentation dans le processus, mais s'il s'agit d'un fichier xml ou insensible à l'indentation, cela ne devrait pas poser de problème. Étant donné que ce script utilise un lecteur RAM, l'écriture et la lecture de ces fichiers temporaires sont aussi rapides que de travailler en mémoire.
la source
De plus, si vous souhaitez simplement exclure certaines lignes avant un marqueur donné, vous pouvez utiliser:
(Glenn Jackman sur /programming//a/1492538 )
En canalisant certaines commandes, vous pouvez obtenir le comportement avant / après:
la source
awk
sur un fichier inversé pour gérer les lignes suivantes lorsque vous voulez affecter les lignes avant et inverser le résultat.Une façon d'y parvenir, peut-être la manière la plus simple serait de créer une variable et de faire ce qui suit:
De cette façon, vous avez toujours votre structure. Et vous pouvez facilement voir de la doublure que vous essayez de supprimer.
la source
S'il n'y a qu'un seul match:
Sinon (awk):
la source