Obtention du text du dernier marqueur à EOF dans POSIX.2

J'ai un text avec des lignes de marqueur comme:

aaa --- bbb --- ccc 

J'ai besoin d'get un text du dernier marqueur (pas inclus) à EOF. Dans ce cas, ce sera

 ccc 

Existe-t-il une manière élégante dans POSIX.2? En ce moment, j'utilise deux exécutions – d'abord avec nl et grep pour la dernière occurrence avec le numéro de ligne respectif. Ensuite, je extrait le numéro de ligne et utilise sed pour extraire le morceau en question.

Les segments de text peuvent être assez volumineux, donc j'ai peur d'utiliser une méthode d'ajout de text comme si nous ajoutions le text à un tampon, si nous rencontrons le marqueur, nous viderons le tampon, de sorte qu'à EOF nous avons notre dernier morceau dans le tampon .

Upd: 5 vues pour 24 heures, donc je vais mettre à jour les balises si incorrectes 🙁

A less que vos segments ne soient vraiment énormes (comme dans: vous ne pouvez vraiment pas épargner autant de RAM, probablement parce que c'est un minuscule système embarqué contrôlant un système de files volumineux), 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 stream, à partir de laquelle toutes datatables lues et non sauvegardées sont perdues. C'est vraiment un travail pour awk, bien que sed puisse le faire aussi.

 sed -n -e 's/^---$//' -e 'ta' \ -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-le. Ou déterminer le décalage d'octet et imprimer à 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 simple version en une seule passe qui s'appuie sur une extension commune à awk qui permet au séparateur d'loggings RS d'être une expression régulière (POSIX ne permet qu'un seul caractère). Ce n'est pas tout à fait correct: si le file se termine par un séparateur d'loggings, il imprime le morceau avant le dernier séparateur d'loggings au lieu d'un logging 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}' 

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:

 $ LINE=`awk '/^---$/{n=NR}END{print n}' file` 

pour get le numéro de ligne. Et puis écho tout le text à partir de ce numéro de ligne avec:

 $ awk "NR>$LINE" file 

Cela ne devrait pas nécessiter de mise en memory tampon excessive.

 lnum=$(($(sed -n '/^---$/=' file | sed '$!d') +1)); sed -n "${lnum},$ p" file 

Les premiers numéros de ligne des sorties sed lignes "—" …
Le second sed extrait le dernier numéro de la sortie du premier sed …
Ajoutez 1 à ce numéro pour get le début de votre bloc "ccc" …
Le troisième 'sed' sort du début du bloc "ccc" à EOF

Mise à jour (avec les informations modifiées sur les methods Gilles)

Eh bien, je me demandais comment le tac glenn jackman fonctionnerait, alors j'ai testé les trois réponses (au moment de l'écriture) … Le (s) file (s) de test contenait chacun 1 million de lignes (de leur propre numéro de ligne).
Toutes les réponses ont fait ce qui était attendu …

Voici les time ..


Gilles sed (passe simple)

 # real 0m0.470s # user 0m0.448s # sys 0m0.020s 

Gilles awk (passe simple)

 # very slow, but my data had a very large data block which awk needed to cache. 

Gilles 'two-pass' (première méthode)

 # real 0m0.048s # user 0m0.052s # sys 0m0.008s 

Gilles 'two-pass' (deuxième méthode) … très rapide

 # real 0m0.204s # user 0m0.196s # sys 0m0.008s 

Gilles 'two-pass' (troisième méthode)

 # real 0m0.774s # user 0m0.688s # sys 0m0.012s 

Gilles 'gawk' (méthode RT) … très rapide , mais ce n'est pas POSIX.

 # real 0m0.221s # user 0m0.200s # sys 0m0.020s 

glenn jackmantrès rapide , mais ce n'est pas POSIX.

 # real 0m0.022s # user 0m0.000s # sys 0m0.036s 

fred.bear

 # real 0m0.464s # user 0m0.432s # sys 0m0.052s 

Mackie Messer

 # real 0m0.856s # user 0m0.832s # sys 0m0.028s 

Utilisez " tac " qui affiche les lignes d'un file de bout en bout:

 tac afile | awk '/---/ {exit} {print}' | tac