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 -d
dé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
nl
peut ê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 -ha
signifie donc numéroter toutes les lignes d'en- tête et -bn
signifie pas de lignes de corps - comme cela commence dans un corps état de .
Jusqu'à ce que j'apprenne cela, je l'utilisais nl
pour n'importe quelle entrée, mais après avoir réalisé que cela nl
pouvait 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 nl
peut être très utilement appliqué à d'autres égards - comme celui-ci - si vous modifiez juste un peu son entrée - comme je le fais sed
ci-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 nl
lignes 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 ( -w
nombre d' -s
idé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 nl
cela divisera son entrée en sections logiques pour vous et que vous pouvez insérer des -s
chaî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 nl
n'est pas votre application cible, un GNU sed
peut e
exé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 sed
recueille les données d'entrée dans l'espace de motif jusqu'à ce qu'il en ait assez pour réussir la substitution T
et arrêter le b
ranching vers l' :l
abel. Quand il le fait, il e
xécute nl
avec 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 sed
essayée de correspondre - donc cette commande remplace la @@.*start
ligne entière à la nl <<\\@@
place.
:l;N
- La
:
commande définit une étiquette de branche - ici j'en ai défini une nommée :l
abel. La N
commande 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 sed
espace de motif - le \n
caractère de ligne électronique est un délimiteur sûr pour un sed
der 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 \n
n'agira que sur un espace de motif dans lequel la dernière ligne électronique est immédiatement suivie en @@.*end
marquant la toute fin$
de l'espace de motif. Lorsqu'il agit, il remplace toute la chaîne correspondante par le \1
premier \(
groupe \)
, ou \n@@
.
Tl
- la
T
commande 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 T
commande est échoue et se ramifie vers l' :l
abel, ce qui entraîne l' sed
extraction de la N
ligne 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 T
est échoué , exécutera une commande qui ressemble à ceci:sed
e
l
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 read
boucle, mais pour une bonne raison. Le shell - (plus particulièrement un bash
shell) - 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 read
un 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 read
tirer une ligne. Si elle réussit, cela signifie que la boucle n'a pas encore atteint EOF et que dans le cas case
elle correspond à un délimiteur de début, le do
bloc est immédiatement exécuté. Sinon, printf
imprime le$line
on read
et sed
est appelé.
sed
va p
rimer chaque ligne jusqu'à ce qu'il rencontre le début marqueur - quand il q
uits entièrement entrée. Le -u
commutateur nbuffered est nécessaire pour GNU sed
car il peut tamponner plutôt avidement sinon, mais - selon les spécifications - les autres POSIX sed
devraient fonctionner sans aucune considération particulière - tant qu'il <infile
s'agit d'un fichier normal.
Lors de la première sed
q
sortie, le shell exécute le do
bloc de la boucle - qui en appelle un autre sed
qui 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
, head
et sed
de 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.
nl
n'a pas à accumuler d'état . Regardeznl -d
et vérifiez vosman
/info
pages pour plus d'informations surnl
le délimiteur de section .