Vous pouvez le faire comme suit :
$ sed -e '
/BEGIN/,/END/!d
H;/BEGIN/h;/END/!d;g
' inp
Comment cela fonctionne, pour la plage de lignes de début/fin, il les stocke dans l'espace de maintien. Supprime ensuite jusqu'à ce que vous rencontriez la ligne END. À quel point nous rappelons ce qui est en attente. OTW, nous n'obtenons rien.HTH.
cat input |
sed '/\*\*\*\*\* BEGIN \*\*\*\*\*/,/\*\*\*\*\* END *\*\*\*\*/ p;d' |
tac |
sed '/\*\*\*\*\* END \*\*\*\*\*/,/\*\*\*\*\* BEGIN *\*\*\*\*/ p;d' |
tac
Cela fonctionne en ayant tac
inverser les lignes pour que sed
peut trouver les deux délimiteurs dans les deux ordres.
Avec pcregrep
:
pcregrep -M '(?s)BEGIN.*?END'
Cela fonctionne également si BEGIN et END sont sur la même ligne, mais pas dans des cas comme :
BEGIN 1 END foo BEGIN 2
END
Où pcregrep
attrape le premier BEGIN 1 END
, mais pas le second.
Pour les gérer, avec awk
, vous pourriez faire :
awk '
!inside {
if (match($0, /^.*BEGIN/)) {
inside = 1
remembered = substr($0, 1, RLENGTH)
$0 = substr($0, RLENGTH + 1)
} else next
}
{
if (match($0, /^.*END/)) {
print remembered $0
if (substr($0, RLENGTH+1) ~ /BEGIN/)
remembered = ""
else
inside = 0
} else
remembered = remembered $0 ORS
}'
Sur une entrée comme :
a
BEGIN blah END BEGIN 1
2
END
b
BEGIN foo END
c
BEGIN
bar
END BEGIN
baz END
d
BEGIN
xxx
Cela donne :
BEGIN blah END BEGIN 1
2
END
BEGIN foo END
BEGIN
bar
END BEGIN
baz END
Les deux ont besoin de tout stocker du BEGIN au END suivant en mémoire. Donc si vous avez un gros fichier dont la première ligne contient BEGIN mais sans END, tout le fichier sera stocké en mémoire pour rien.
Le seul moyen de contourner cela serait de traiter le fichier deux fois, mais bien sûr, cela ne peut être fait que lorsque l'entrée est un fichier normal (pas un tube par exemple).