Sono d'accordo con te - probabilmente è un problema generico. Tuttavia, alcune utility comuni hanno alcune funzionalità per gestirlo.
nl
nl
, ad esempio, separa l'input in pagine logiche come -d
eliminato da un delimitatore di sezione a due caratteri . Tre occorrenze su una riga indicano da sole l'inizio di un'intestazione , due il corpo e uno il piè di pagina . Sostituisce uno qualsiasi di questi trovati nell'input con una riga vuota nell'output - che sono le uniche righe vuote che abbia mai stampato
Ho modificato il tuo esempio per includere un'altra sezione e inserirlo ./infile
. Quindi sembra così:
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
Quindi ho eseguito il seguente:
sed 's/^@@.*start$/@@@@@@/
s/^@@.*end$/@@/' <infile |
nl -d@@ -ha -bn -w1
nl
si può dire di accumulare stato attraverso pagine logiche, ma non per impostazione predefinita. Invece numererà le linee del suo input secondo gli stili e per sezione . Quindi -ha
significa numerare tutte le linee di intestazione e -bn
significa nessuna linea del corpo - come inizia in un corpo dello Stato.
Fino a quando non l'ho imparato, lo usavo nl
per qualsiasi input, ma dopo aver realizzato che ciò nl
poteva distorcere l'output in base al suo -d
elimitatore predefinito, \:
ho imparato a stare più attento e ho iniziato a utilizzare l' grep -nF ''
input non testato. Ma un'altra lezione appresa quel giorno è stata che nl
può essere utilmente applicato sotto altri aspetti - come questo - se modifichi solo un po 'il suo input - come faccio sed
sopra.
PRODUZIONE
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
Ecco qualcosa in più nl
: noti sopra come tutte le linee tranne quelle numerate iniziano con gli spazi? Quando le nl
linee dei numeri inserisce un certo numero di caratteri nella testa di ciascuno. Per quelle righe non numera - nemmeno gli spazi vuoti - corrisponde sempre al rientro inserendo (-w
numera idth count + eparator -s
len) * spazi all'inizio di linee non numerate. Ciò consente di riprodurre esattamente il contenuto non numerato confrontandolo con il contenuto numerato e con poco sforzo. Se consideri che nl
dividerà il suo input in sezioni logiche per te e che puoi inserire -s
stringhe arbitrarie all'inizio di ogni riga che numera, allora diventa abbastanza facile gestire il suo output:
sed 's/^@@.*start$/@@@@@@/
s/^@@.*end/@@/; t
s/^\(@@\)\{1,3\}$/& /' <infile |
nl -d@@ -ha -bn -s' do something with the next line!
'
Le stampe sopra ...
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
GNU sed
Se nl
non è l'applicazione di destinazione, una GNU sed
può e
eseguire un comando di shell arbitrario per te a seconda di una corrispondenza.
sed '/^@@.*start$/!b
s//nl <<\\@@/;:l;N
s/\(\n@@\)[^\n]*end$/\1/
Tl;e' <infile
Sopra sed
raccoglie input nello spazio modello fino a quando non ha abbastanza per passare con successo l' T
est di sostituzione e interrompere b
il :l
ranching all'abel. Quando lo fa, viene e
elaborato nl
con input rappresentato come un <<
documento qui per tutto il resto del suo spazio-modello.
Il flusso di lavoro è così:
/^@@.*start$/!b
- se un
^
intera linea $
non !
non /
corrisponde /
al modello di cui sopra, allora è b
un ranch fuori dalla sceneggiatura e autoprinted - quindi da questo punto in poi stiamo lavorando solo con una serie di linee che ha avuto inizio con il modello.
s//nl <<\\@@/
- il
s//
campo vuoto sostituisce l' /
ultimo indirizzo sed
tentato di corrispondere, quindi questo comando sostituisce invece l'intera @@.*start
riga nl <<\\@@
.
:l;N
- Il
:
comando definisce un'etichetta di ramo - qui ho impostato uno chiamato :l
abel. Il N
comando ext aggiunge la riga successiva di input allo spazio pattern seguito da un \n
carattere ewline. Questo è uno dei pochi modi per ottenere una \n
ewline in uno sed
spazio modello: il \n
personaggio ewline è un delimitatore sicuro per un sed
der che lo ha fatto per un po '.
s/\(\n@@\)[^\n]*end$/\1/
- questo
s///
ubstitution può avere successo soltanto dopo un avvio viene rilevato e solo sulla prima a seguito del verificarsi di un fine linea. \n
Agirà solo su uno spazio modello in cui la linea finale finale è immediatamente seguita @@.*end
contrassegnando la fine $
dello spazio modello. Quando agisce, sostituisce l'intera stringa corrispondente con il \1
primo \(
gruppo \)
o \n@@
.
Tl
- il
T
comando est si ramifica su un'etichetta (se fornita) se non si è verificata una sostituzione riuscita dall'ultima volta in cui una riga di input è stata trascinata nello spazio modello (come faccio io con / N
) . Ciò significa che ogni volta che una \n
ewline viene aggiunta allo spazio del pattern che non corrisponde al delimitatore finale, il T
comando est ha esito negativo e si ramifica di nuovo :l
sull'abel, il che si traduce in sed
pull in N
ext line e looping fino al successo.
e
Quando la sostituzione per la partita finale è successo e lo script non lo fa di nuovo ramo per un fallito T
est, sed
sarà e
xecute un comando che l
ooks come questo:
nl <<\\@@\nline X\nline Y\nline Z\n@@$
Puoi vederlo da solo modificando l'ultima riga lì per apparire Tl;l;e
.
Stampa:
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
Un ultimo modo per farlo, e forse il modo più semplice, è usare un while read
loop, ma per una buona ragione. La shell - (soprattutto una bash
shell) - è in genere piuttosto spaventosa nel gestire input in grandi quantità o in flussi costanti. Anche questo ha senso: il lavoro della shell è gestire l'input carattere per carattere e richiamare altri comandi in grado di gestire le cose più grandi.
Ma soprattutto sul suo ruolo c'è che la shell non deve read
sovrastare gran parte dell'input - è specificato per non bufferizzare l'input o l'output al punto che consuma così tanto o non trasmette abbastanza nel tempo che i comandi che chiama rimangono mancanti - al byte. Quindi read
rende un test di input eccellente - per return
informazioni sulla presenza di input rimanenti e si dovrebbe richiamare il comando successivo per leggerlo - ma in genere non è il modo migliore di procedere.
Ecco un esempio, tuttavia, di come si potrebbero usare read
e altri comandi per elaborare l'input in sincronia:
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 prima cosa che accade per ogni iterazione è read
una riga. Se ha esito positivo significa che il loop non ha ancora colpito EOF e quindi nel case
corrispondente delimitatore di avvio il do
blocco viene immediatamente eseguito. Altrimenti, printf
stampa $line
l'esso read
e sed
si chiama.
sed
si p
Rint ogni riga finché non incontra l' inizio marcatore - quando q
UITS ingresso interamente. Lo -u
switch nbuffered è necessario per GNU sed
perché altrimenti può bufferizzare avidamente, ma - secondo le specifiche - altri POSIX sed
dovrebbero funzionare senza alcuna considerazione speciale - purché <infile
sia un file normale.
Quando il primo viene sed
q
attivato, la shell esegue il do
blocco del loop, che chiama un altro sed
che stampa ogni riga fino a quando non incontra il marker di fine . Inoltra il suo output a paste
, perché stampa i numeri di riga ciascuno sulla propria riga. Come questo:
1
line M
2
line N
3
line O
paste
quindi li incolla sui :
personaggi e l'intero output appare come:
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
Questi sono solo esempi: qui si può fare qualsiasi cosa nel test o fare blocchi, ma la prima utility non deve consumare troppo input.
Tutte le utility coinvolte leggono lo stesso input - e stampano i loro risultati - ognuna a turno. Questo genere di cose può essere difficile da ottenere il blocco di - perché diversi programmi di utilità saranno tamponare più di altri - ma si possono generalmente contare su dd
, head
e sed
di fare la cosa giusta (anche se, per GNU sed
, è necessario il cli-switch) e dovresti sempre essere in grado di fare affidamento read
, perché per sua natura è molto lento . Ed è per questo che il loop sopra lo chiama solo una volta per blocco di input.
nl
non deve accumulare stato . Guardatenl -d
e controllare leman
/info
pagine per le informazioni sulnl
's delimitatore sezione .