Comment puis-je découper un fichier (flux d'entrée bien) afin que je ne reçoive que les lignes allant de la première occurrence de motif foo
à la dernière occurrence de motif bar
?
Par exemple, considérez l'entrée suivante:
A line
like
foo
this
foo
bar
something
something else
foo
bar
and
the
rest
J'attends cette sortie:
foo
this
foo
bar
something
something else
foo
bar
text-processing
sed
rahmu
la source
la source
foo
et le dernierbar
et imprimez tout le reste , le cas échéant. Avec un flux, vous devrez lire jusqu'au premierfoo
et mettre en mémoire tampon toutes les lignes suivantes jusqu'à EOF, en vidant le tampon chaque fois que abar
est vu. Cela pourrait signifier la mise en mémoire tampon de l'ensemble du flux en mémoire.Réponses:
La correspondance des motifs sed
/first/,/second/
lit les lignes une par une. Lorsqu'une ligne correspond à/first/
elle, elle s'en souvient et attend avec impatience la première correspondance pour le/second/
motif. En même temps, il applique toutes les activités spécifiées pour ce modèle. Après ce processus recommence encore et encore jusqu'à la fin du fichier.Ce n'est pas ce dont nous avons besoin. Nous devons rechercher la dernière correspondance de
/second/
motif. Par conséquent, nous construisons une construction qui ne recherche que la première entrée/foo/
. Une fois trouvé, le cyclea
commence. Nous ajoutons une nouvelle ligne au tampon de correspondance avecN
et vérifions si elle correspond au modèle/bar/
. Si c'est le cas, nous l'imprimons et effaçons le tampon de correspondance et sautons janyway au début du cycle avecba
.Nous devons également supprimer le symbole de nouvelle ligne après le nettoyage du tampon
/^\n/s/^\n//
. Je suis sûr qu'il existe une bien meilleure solution, malheureusement, cela ne m'est pas venu à l'esprit.J'espère que tout est clair.
la source
sed
versions, par exemple BSD sed (qui se trouve sur Mac), les balises doivent être suivies d'une nouvelle ligne ou d'une fin de chaîne, donc le réglage suivant est nécessaire:sed -n -e '/foo/{:a' -e 'N;/^\n/s/^\n//;/bar/{p;s/.*//;};ba' -e '};'
Cela fonctionne également sur GNU sed, donc je pense que cette modification (plusieurs-e
arguments mettre fin à un argument après chaque nom de branche) est une bonne habitude portable à prendre lorsque vous utilisez des branches dans sed.Je le ferais avec une petite doublure Perl.
les rendements
la source
E
placee
et-00777
au lieu du$/
bit (voir perlrun (1)). Ce qui le raccourcirait en:,perl -0777 -nE 'say /(foo.*bar)/s'
toujours en quelque sorte lisible.-0[octal]
je trouverai surtout sa voie dans mon flux de travail! Merci pour celaVoici une solution GNU sed à deux passes qui ne nécessite pas beaucoup de mémoire:
Explication
sed
invocation passe infile et trouve la première occurrence defoo
et toutes les occurrences suivantes debar
.sed
script avec deux invocations desed
et unetr
. La sortie du troisièmesed
est[start_address],[end_address]p
, sans les crochets.sed
passe àinfile
nouveau, imprimant les adresses trouvées et tout le reste.la source
Si le fichier d'entrée tient confortablement en mémoire, restez simple .
Si le fichier d'entrée est énorme, vous pouvez l'utiliser
csplit
pour le diviser en morceaux au premierfoo
et à tous les suivants,bar
puis assembler les morceaux. Les morceaux sont appeléspiece-000000000
,piece-000000001
etc. Choisissez un préfixe (icipiece-
) qui ne se heurtera pas aux autres fichiers existants.(Sur les systèmes non Linux, vous devrez utiliser un grand nombre à l'intérieur des accolades, par exemple
{999999999}
, et passer l'-k
option. Ce nombre est le nombre debar
pièces.)Vous pouvez assembler toutes les pièces avec
cat piece-*
, mais cela vous donnera tout après le premierfoo
. Retirez donc cette dernière pièce en premier. Étant donné que les noms de fichiers produits parcsplit
ne contiennent aucun caractère spécial, vous pouvez les retravailler sans prendre de précaution particulière, par exemple avecou équivalent
Vous pouvez maintenant joindre toutes les pièces et supprimer les fichiers temporaires:
Si vous souhaitez supprimer les morceaux au fur et à mesure qu'ils sont concaténés pour économiser de l'espace disque, faites-le en boucle:
la source
Voici une autre façon avec
sed
:Il ajoute chaque ligne de la
/foo/,$
plage (les lignes!
ne faisant pas partie de cette plage sontd
supprimées) à l'H
ancien espace. Les lignes qui ne correspondent pasbar
sont ensuite supprimées. Sur les lignes qui correspondent, l'espace de motif est vidé, ex
changé avec l'espace de maintien et la ligne vide de tête dans l'espace de motif est supprimée.Avec une entrée énorme et peu d'occurrences,
bar
cela devrait être (beaucoup) plus rapide que de tirer chaque ligne dans l'espace de motif et, à chaque fois, de vérifier l'espace de motifbar
.Expliqué:
Bien sûr, s'il s'agit d'un fichier (et qu'il tient en mémoire), vous pouvez simplement exécuter:
car
ed
peut rechercher en avant et en arrière.Vous pouvez même lire une sortie de commande dans le tampon de texte si votre shell prend en charge la substitution de processus:
ou si ce n'est pas le cas, avec
gnu ed
:la source
En utilisant n'importe quel awk dans n'importe quel shell sur n'importe quel système UNIX et sans lire le fichier entier ou le flux d'entrée en mémoire à la fois:
la source
Grep pourrait le faire aussi (enfin, GNU grep):
Pour l'apport du corps de la question:
la source