Je suis d' accord avec vous - il probablement est un problème générique. Cependant, certains utilitaires communs ont certaines installations pour le gérer.
nl
nl, par exemple, sépare l'entrée en pages logiques comme -ddélimitées par un délimiteur de section à deux caractères . Trois occurrences sur une ligne indiquent à elles seules le début d'un cap , deux le corps et un le pied de page . Il remplace tous ceux trouvés en entrée par une ligne vierge en sortie - qui sont les seules lignes vierges qu'il imprime
J'ai modifié votre exemple pour inclure une autre section et je l'ai ajouté ./infile. Il ressemble donc à ceci:
line A
line B
@@inline-code-start
line X
line Y
line Z
@@inline-code-end
line C
line D
@@start
line M
line N
line O
@@end
Ensuite, j'ai exécuté ce qui suit:
sed 's/^@@.*start$/@@@@@@/
s/^@@.*end$/@@/' <infile |
nl -d@@ -ha -bn -w1
nlpeut être chargé d' accumuler l'état sur les pages logiques, mais ce n'est pas le cas par défaut. Au lieu de cela, il numérotera les lignes de son entrée en fonction des styles et par section . Cela -hasignifie donc numéroter toutes les lignes d'en- tête et -bnsignifie pas de lignes de corps - comme cela commence dans un corps état de .
Jusqu'à ce que j'apprenne cela, je l'utilisais nlpour n'importe quelle entrée, mais après avoir réalisé que cela nlpouvait fausser la sortie selon son -délimiteur par défaut, \:j'ai appris à être plus prudent avec lui et j'ai commencé à utiliser grep -nF ''à la place pour une entrée non testée. Mais une autre leçon apprise ce jour-là est que cela nlpeut être très utilement appliqué à d'autres égards - comme celui-ci - si vous modifiez juste un peu son entrée - comme je le fais sedci-dessus.
PRODUCTION
line A
line B
1 line X
2 line Y
3 line Z
line C
line D
1 line M
2 line N
3 line O
Voici un peu plus nl- remarquez-vous ci-dessus comment toutes les lignes sauf celles numérotées commencent par des espaces? Lorsque des nllignes numérotées, il insère un certain nombre de caractères dans la tête de chacun. Pour ces lignes, il ne numérote pas - même les blancs - il correspond toujours au retrait en insérant ( -wnombre d' -sidées + séparateur len) * des espaces en tête des lignes non numérotées. Cela vous permet de reproduire le contenu non numéroté exactement en le comparant au contenu numéroté - et avec peu d'effort. Lorsque vous considérez que nlcela divisera son entrée en sections logiques pour vous et que vous pouvez insérer des -schaînes arbitraires en tête de chaque ligne numérotée, il devient assez facile de gérer sa sortie:
sed 's/^@@.*start$/@@@@@@/
s/^@@.*end/@@/; t
s/^\(@@\)\{1,3\}$/& /' <infile |
nl -d@@ -ha -bn -s' do something with the next line!
'
Les impressions ci-dessus ...
line A
line B
1 do something with the next line!
line X
2 do something with the next line!
line Y
3 do something with the next line!
line Z
line C
line D
1 do something with the next line!
line M
2 do something with the next line!
line N
3 do something with the next line!
line O
GNOU sed
Si ce nln'est pas votre application cible, un GNU sedpeut eexécuter une commande shell arbitraire pour vous en fonction d'une correspondance.
sed '/^@@.*start$/!b
s//nl <<\\@@/;:l;N
s/\(\n@@\)[^\n]*end$/\1/
Tl;e' <infile
Ci-dessus sedrecueille les données d'entrée dans l'espace de motif jusqu'à ce qu'il en ait assez pour réussir la substitution Tet arrêter le branching vers l' :label. Quand il le fait, il exécute nlavec une entrée représentée comme un <<document ici pour tout le reste de son espace de motif.
Le workflow est comme ceci:
/^@@.*start$/!b
- si un
^ ligne entière $ne correspond !pas au modèle ci-dessus, elle est retirée du script et imprimée automatiquement - donc à partir de ce moment, nous ne travaillons qu'avec une série de lignes commençant par le modèle.//b
s//nl <<\\@@/
- le vide
s// champ/ correspond à la dernière adresse sedessayée de correspondre - donc cette commande remplace la @@.*startligne entière à la nl <<\\@@place.
:l;N
- La
:commande définit une étiquette de branche - ici j'en ai défini une nommée :label. La Ncommande ext ajoute la ligne d'entrée suivante à l'espace de motif suivie d'un\n caractère de ligne électronique. C'est l'une des rares façons d'obtenir une ligne \nélectronique dans un sedespace de motif - le \ncaractère de ligne électronique est un délimiteur sûr pour un sedder qui le fait depuis un certain temps.
s/\(\n@@\)[^\n]*end$/\1/
- cette
s///substitution ne peut être réussie qu'après un début et uniquement lors de la première occurrence suivante d'une ligne de fin . Il \nn'agira que sur un espace de motif dans lequel la dernière ligne électronique est immédiatement suivie en @@.*endmarquant la toute fin$ de l'espace de motif. Lorsqu'il agit, il remplace toute la chaîne correspondante par le \1premier \(groupe \), ou \n@@.
Tl
- la
Tcommande est se branche sur une étiquette (si elle est fournie) si une substitution réussie ne s'est pas produite depuis la dernière fois qu'une ligne d'entrée a été tirée dans l'espace modèle (comme je le fais avec N) . Cela signifie que chaque fois qu'une ligne \nélectronique est ajoutée à un espace de modèle qui ne correspond pas à votre délimiteur de fin, la Tcommande est échoue et se ramifie vers l' :label, ce qui entraîne l' sedextraction de la Nligne ext et la boucle jusqu'à ce qu'elle réussisse.
e
Lorsque la substitution de la correspondance de fin est réussie et que le script ne se ramifie pas pour un Test échoué , exécutera une commande qui ressemble à ceci:sedel
nl <<\\@@\nline X\nline Y\nline Z\n@@$
Vous pouvez le constater par vous-même en modifiant la dernière ligne pour ressembler à Tl;l;e .
Il imprime:
line A
line B
1 line X
2 line Y
3 line Z
line C
line D
1 line M
2 line N
3 line O
while ... read
Une dernière façon de le faire, et peut-être la manière la plus simple, est d'utiliser une while readboucle, mais pour une bonne raison. Le shell - (plus particulièrement un bashshell) - est généralement assez épouvantable pour gérer les entrées en grandes quantités ou en flux réguliers. Cela a également du sens - le travail du shell est de gérer l'entrée caractère par caractère et d'appeler d'autres commandes qui peuvent gérer les choses plus importantes.
Mais ce qui est important à propos de son rôle, c'est que le shell ne doit pas read occuper une grande partie de l'entrée - il est spécifié de ne pas mettre en mémoire tampon d'entrée ou de sortie au point qu'il consomme tellement ou qu'il ne relaie pas suffisamment à temps que les commandes qu'il appelle sont laissées manquantes. - à l'octet. Fait donc readun excellent test d' entrée - pourreturn savoir s'il reste des entrées et vous devez appeler la prochaine commande pour la lire - mais ce n'est généralement pas la meilleure façon de procéder.
Voici un exemple, cependant, de la façon dont on pourrait utiliser read et d' autres commandes pour traiter l'entrée en synchronisation:
while IFS= read -r line &&
case $line in (@@*start) :;; (*)
printf %s\\n "$line"
sed -un "/^@@.*start$/q;p";;
esac;do sed -un "/^@@.*end$/q;=;p" |
paste -d: - -
done <infile
La première chose qui se produit pour chaque itération est de readtirer une ligne. Si elle réussit, cela signifie que la boucle n'a pas encore atteint EOF et que dans le cas caseelle correspond à un délimiteur de début, le dobloc est immédiatement exécuté. Sinon, printfimprime le$line on readet sedest appelé.
sedva primer chaque ligne jusqu'à ce qu'il rencontre le début marqueur - quand il quits entièrement entrée. Le -ucommutateur nbuffered est nécessaire pour GNU sedcar il peut tamponner plutôt avidement sinon, mais - selon les spécifications - les autres POSIX seddevraient fonctionner sans aucune considération particulière - tant qu'il <infiles'agit d'un fichier normal.
Lors de la première sed qsortie, le shell exécute le dobloc de la boucle - qui en appelle un autre sedqui imprime chaque ligne jusqu'à ce qu'il rencontre le marqueur de fin . Il dirige sa sortie verspaste , car il imprime chacun des numéros de ligne sur leur propre ligne. Comme ça:
1
line M
2
line N
3
line O
paste puis colle ceux ensemble sur : caractères, et la sortie entière ressemble à ceci:
line A
line B
1:line X
2:line Y
3:line Z
line C
line D
1:line M
2:line N
3:line O
Ce ne sont que des exemples - tout peut être fait dans le test ou dans les blocs ici, mais le premier utilitaire ne doit pas consommer trop d'entrée.
Tous les utilitaires impliqués lisent la même entrée - et impriment leurs résultats - chacun à leur tour. Ce genre de chose peut être difficile d'obtenir le blocage de - parce que différents utilitaires tampon plus que d' autres - mais vous pouvez généralement compter sur dd, headet sedde faire la bonne chose (bien que, pour GNU sed, vous avez besoin du cli-switch) et vous devriez toujours pouvoir compter read- car c'est, par nature, très lent . Et c'est pourquoi la boucle ci-dessus ne l'appelle qu'une seule fois par bloc d'entrée.
nln'a pas à accumuler d'état . Regardeznl -det vérifiez vosman/infopages pour plus d'informations surnlle délimiteur de section .