À moins que vos segments ne soient vraiment énormes (comme dans: vous ne pouvez vraiment pas épargner autant de RAM, probablement parce que c'est un petit système embarqué contrôlant un grand système de fichiers), une seule passe est vraiment la meilleure approche. Non seulement parce que ce sera plus rapide, mais surtout parce qu'il permet à la source d'être un flux, à partir duquel toutes les données lues et non enregistrées sont perdues. C'est vraiment un travail pour awk, bien que sed puisse le faire aussi.
sed -n -e 's/^---$//' -e 't a' \
-e 'H' -e '$g' -e '$s/^\n//' -e '$p' -e 'b' \
-e ':a' -e 'h' # you are not expected to understand this
awk '{if (/^---$/) {chunk=""} # separator ==> start new chunk
else {chunk=chunk $0 RS}} # append line to chunk
END {printf "%s", chunk}' # print last chunk (without adding a newline)
Si vous devez utiliser une approche en deux passes, déterminez le décalage de ligne du dernier séparateur et imprimez à partir de cela. Ou déterminez le décalage en octets et imprimez à partir de cela.
</input/file tail -n +$((1 + $(</input/file # print the last N lines, where N=…
grep -n -e '---' | # list separator line numbers
tail -n 1 | # take the last one
cut -d ':' -f 1) )) # retain only line number
</input/file tail -n +$(</input/file awk '/^---$/ {n=NR+1} END {print n}')
</input/file tail -c +$(</input/file LC_CTYPE=C awk '
{pos+=length($0 RS)} # pos contains the current byte offset in the file
/^---$/ {last=pos} # last contains the byte offset after the last separator
END {print last+1} # print characters from last (+1 because tail counts from 1)
')
Addendum: Si vous avez plus de POSIX, voici une version simple en un seul passage qui s'appuie sur une extension commune à awk qui permet au séparateur d'enregistrement RS
d'être une expression régulière (POSIX n'autorise qu'un seul caractère). Ce n'est pas tout à fait correct: si le fichier se termine par un séparateur d'enregistrements, il imprime le bloc avant le dernier séparateur d'enregistrements au lieu d'un enregistrement vide. La deuxième version utilisant RT
évite ce défaut, mais RT
est spécifique à GNU awk.
awk -vRS='(^|\n)---+($|\n)' 'END{printf $0}'
gawk -vRS='(^|\n)---+($|\n)' 'END{if (RT == "") printf $0}'
Gilles 'SO- arrête d'être méchant'
la source
sed
fonctionne bien, mais je n'arrive pas à faire fonctionner l'awk
exemple; il se bloque ... et j'obtiens une erreur dans le 3ème exemple:cut -f ':' -t 1
... cut: option invalide - 't'cut
exemple. Je ne vois rien de mal avec l'awk
exemple, quelle version d'awk utilisez-vous et quelle est votre entrée de test?awk
version fonctionne .. cela prend très longtemps sur un gros fichier .. lased
version a traité le même fichier en 0.470s ... Mes données de test sont très pondérées ... seulement deux morceaux avec un seul '---' trois lignes à partir de la fin du million de lignes ...Une stratégie en deux passes semble être la bonne chose. Au lieu de sed j'utiliserais
awk(1)
. Les deux passes pourraient ressembler à ceci:pour obtenir le numéro de ligne. Et puis faites écho à tout le texte à partir de ce numéro de ligne avec:
Cela ne devrait pas nécessiter une mise en mémoire tampon excessive.
la source
awk -v line=$(awk '/^---$/{n=NR}END{print n}' file) 'NR>line' file
Les premiers
sed
numéros de ligne des sorties des lignes "---" ...Le second
sed
extrait le dernier numéro de la sortie du premier sed ...Ajoutez 1 à ce numéro pour obtenir le début de votre bloc "ccc" ...
Le troisième sorties 'sed' du début du bloc "ccc" à EOF
Mise à jour (avec des informations modifiées sur les méthodes de Gilles)
Eh bien, je me demandais comment Glenn Jackman
tac
se comporterait, alors j'ai testé les trois réponses (au moment de la rédaction) ... Le ou les fichiers de test contenaient chacun 1 million de lignes (de leurs propres numéros de ligne).Toutes les réponses ont fait ce qui était attendu ...
Voici les temps ..
Gilles
sed
(passage unique)Gilles
awk
(passage unique)Gilles en deux passes (première méthode)
Gilles en deux passes (deuxième méthode) ... très rapide
Gilles en deux passes (troisième méthode)
Gilles 'gawk' (méthode RT) ... très rapide , mais ce n'est pas POSIX.
glenn jackman ... très rapide , mais ce n'est pas POSIX.
fred.bear
Mackie Messer
la source
Utilisez " tac " qui sort les lignes d'un fichier de la fin au début:
la source
tac
n'est pas POSIX, il est spécifique à Linux (il est dans les coreutils GNU et dans certaines installations de busybox).Vous pouvez simplement utiliser
ed
Comment ça marche:
t
duplique la ligne courante (.
) - qui est toujours la dernière ligne aued
démarrage (juste au cas où le délimiteur est présent sur la dernière ligne),1,?===?d
supprime toutes les lignes jusqu'à et incluant la correspondance précédente (ed
est toujours sur la dernière ligne )$d
supprime ensuite la dernière ligne (en double),,p
imprime le tampon de texte (remplacez parw
pour éditer le fichier en place) etq
quitte finalemented
.Si vous savez qu'il y a au moins un délimiteur dans l'entrée (et ne vous souciez pas s'il est également imprimé), alors
serait le plus court.
Fonctionnement: il ajoute toutes les lignes à l'
H
ancien tampon, il écrase l'h
ancien tampon lorsqu'il rencontre une correspondance, ild
supprime toutes les lignes sauf la dernière$
lorsqu'ilx
change de tampon (et s'imprime automatiquement).la source