web-dev-qa-db-fra.com

Comment imprimer la dernière séquence de lignes entre un démarrage et un motif final?

Réponses à cette question:

Comment les lignes Grep entre le modèle de début et de fin?

ne sont pas concernés par de multiples séquences de lignes qui tombent entre les modèles de correspondance. Ainsi, par exemple, sed -n '/startpattern_here/,/endpattern_here/p' Imprimera plusieurs séquences de lignes qui se situent entre les occurrences de ces motifs.

Cependant, supposons que je veux imprimer uniquement le dernier de telles séquences dans un fichier. Puis-je faire cela avec SED? Sinon, je suppose probablement awks? Autre chose?

Remarques:

  • Vous pouvez supposer que ces séquences ne se chevauchent pas.
  • Les lignes de motif de démarrage et de fin doivent être incluse dans la sortie.
  • Les réponses faisant des hypothèses de modèles de complexité inférieure sont également valables (bien qu'elles ne soient pas optimales).
9
einpoklum

Voici une solution qui tente de gérer tous les cas, notamment sans blocage de bloc, et être efficace en mémoire et temps d'exécution. Il n'y a pas de ligne d'écriture par ligne dans cette solution, sans traitement de toutes les lignes et aucune tampon de lignes.

#!/bin/bash
    
sp="startpattern_here"
ep="endpattern_here"
f="file"
    
range=$(tac "$f" | grep -n "$sp\|$ep" | awk -F: -v sp="$sp" -v ep="$ep"\
        '$2 ~ sp && prev ~ ep {s=$1; print s,e; exit} {prev=$2; e=$1}')
    
if [[ "$range" ]]; then
    # echo "Counting from the end => start: ${range% *} end: ${range#* }"
    tail -n "${range% *}" "$f" | head -n "${range#* }"
else
    echo "No blocks found" 1>&2
fi

Explication et exemple:

> cat file
startpattern_here
text
endpattern_here
startpattern_here
text
startpattern_here
42
endpattern_here
text
endpattern_here

Dans le pire des cas, nous devons rechercher le fichier entier pour une réponse complète, nous utilisons donc le rapide grep pour cela. Nous commençons à chercher à partir de la fin, il va donc obtenir quelque chose comme ça:

1:endpattern_here
3:endpattern_here
5:startpattern_here
7:startpattern_here
8:endpattern_here
10:startpattern_here

ce qui est pipi à awk pour décider s'il existe un dernier bloc valide ou non. Notez qu'ici awk est utilisé pour une programmation simple, pas pour le traitement du texte réel. Pour une grande entrée, grep est plus rapide que la recherche du fichier avec awk ou encore plus, ligne d'écriture par ligne avec awk ou sed.

De plus, dans le cas où un bloc entre les motifs est détecté rapidement à la fin, awk est en train de sortir et de fermer son tuyau, de sorte que la séquence précédente quitte également, sans rechercher dans l'ensemble du fichier.

De cette façon, nous obtenons la gamme, comptant à partir de la fin, et enfin tail et head recherche () à ces numéros de ligne et "CAT" le contenu. En cas de gamme vide, il n'y a pas de sortie standard.

startpattern_here
42
endpattern_here
1
thanasisp
$ seq 20 > file
$ awk '/5/{rec=""; f=1} f{rec=rec $0 ORS; if (/8/) f=0} END{if (!f) printf "%s", rec}' file
15
16
17
18
0
Ed Morton

Solution SED SIMPLE et SIMPLE SIMPLE. La plupart des autres solutions permettent de gaspiller des ressources en double-tac-ing, voire pire, chargant une entrée entière dans la mémoire à la fois ou en effectuant un traitement de plusieurs passages d'une manière ou d'une autre.

Ceci traite le texte Text-By-ligne, nous n'avons donc besoin que de mémoire pour une copie du bloc correspondant, et nous ne faisons pas la fourchette et n'exige d'autres choses qui feraient encore plus de traitement supplémentaire. En tant que bonus, il est assez lisible et compréhensible (Eh bien, dans la mesure où tout script SED peut être).

Au lieu de votre: sed -n '/startpattern_here/,/endpattern_here/p' Tu fais cela:

sed -n '/startpattern_here/,/endpattern_here/H; /startpattern_here/h; ${g;p}'

Explication (note: n'importe quoi après ; est indépendant des commandes précédentes, sauf grouper avec { et }):

  • première partie /startpattern_here/,/endpattern_here/H est surtout similaire à celui de votre question, mais au lieu d'une impression directe pour tout montage entre les modèles de début et de fin, il ajoute que le texte "maintenir l'espace" (H).

  • /startpattern_here/h Avis lorsque le nouveau match commence et efface l'espace d'attente précédent en l'écrasant (h) avec espace de motif actuel. Notez que la ligne suivante dans le fichier commencera bien sûr l'exécution de toutes nos commandes à partir de zéro, ce qui permettra de conserver l'espace (voir point ci-dessus) - résultat que nous garderons toujours dans l'espace de maintien de l'espace.

  • ${g;p} - $ Adresse des correspondances uniquement sur la dernière ligne dans le fichier, de sorte que quelque chose entre { et } est exécuté uniquement lorsque nous avons terminé avec le fichier de traitement. Ici, nous imprimons simplement du contenu de l'espace de maintien (par g - Copie de l'espace de maintien dans l'espace de motif et p - Espace motif d'impression)

par exemple, pour obtenir le dernier paquet Debian Basic Info:

% sed -n '/^Package/,/^Section/H; /^Package/h; ${g;p}' /var/lib/dpkg/status

Package: zsh-common
Status: install ok installed
Priority: optional
Section: shells

0
Matija Nalis