Come posso usare sed o ex per sostituire un blocco (codice multilinea) con un nuovo blocco di testo (codice)?


9

Devo sostituire un grosso blocco di testo (codice script shell) in un file con un altro blocco di testo.

Sono rimasto impressionato da Come posso usare sed per sostituire una stringa multilinea? risposta di antak e sostituzione multilinea risposta di Bruce Ediger

Ma ho qualche problema ad usarli.

  1. antak ha già menzionato nella sua risposta che lo streaming di file interi ( 1h;2,$H;$!d;g;) sul buffer non è consigliabile per file di grandi dimensioni, poiché sovraccarica la memoria.

  2. So che sedpuò essere utilizzato con la funzione di blocco per mantenere invariato il testo all'esterno del blocco. Voglio usare questa funzione Ma se uso,

    sed -i '/marker1/,/marker2/s/.*/new text (code)/' filename
    

inserirà ripetutamente nuovo testo (codice) per ogni flusso. Quindi devo creare il blocco visivo come un flusso, usando qualcosa di simile a quello suggerito da Antak in precedenza, ma per il blocco (non per l'intero file).

  1. Come accennato da Bruce Ediger, è possibile provare la funzione di aggiunta di exinizio che inizia con la afine di .(punto), ma il mio nuovo testo (codice) contiene righe che iniziano con punto, che può essere considerato il punto della sintassi di aggiunta. Come posso usarlo in questa situazione?

  2. exIl dd"numero di righe" può eliminare più righe, ma se ho un blocco tra / marker1 / e / marker2 / con il numero di righe non fisso (variabile) deve essere sostituito con un nuovo testo (codice), come fare vero?


Trovo il tuo uso del termine "blocco visivo" insolito e confuso. Intendi semplicemente un blocco (gruppo) di linee complete consecutive?
Scott,

Sì, sto modificando la domanda per evitare confusione.
Gibuti

Qual è la fonte per il testo sostitutivo? È in un file o è "solo nella mente dei programmatori"?
don_crissti,

Questo è per il patching situazionale di uno script di shell. Da qui la fonte per il testo di sostituzione è la mente del programmatore (o di uno script maestro sul quale sedo excomando funziona).
omaggio

Don Crissti Avrei usato ril comando read file, per copiare il testo da un altro file, se l'origine del testo sostitutivo si trova in un file.
omaggio

Risposte:


10

Io suggerisco di usare il c di comando hange (che è essenzialmente un d elete accoppiato con un un ppend, anche se l'accodamento viene applicata solo per l'ultima riga della gamma che è esattamente ciò che si vuole qui):

sed -i '/marker1/,/marker2/c\
New text 1\
New text 2' filename

Qui usando sedla sintassi di GNU per l'editing sul posto ( -i). Tale ccomando è altrimenti standard e portatile. GNU sedsupporta:

sed '/marker1/,/marker2/cNew text 1\
New text 2' filename

come estensione non standard.

I caratteri di nuova riga e barra rovesciata devono essere sottoposti a escape (con barra rovesciata) nel testo sostitutivo.


Il comando di cambio funziona bene per entrambi sede exsimili. Grazie mille.
Gibuti

Esiste un modo per evitare "\" e utilizzare qui il documento per sedcome nel caso della exsintassi suggerita da Bruce Ediger nella sostituzione multilinea .
omaggio

Non ti serviranno le barre rovesciate se usi il rcomando discusso in altre risposte a quella domanda. Non sono sicuro di come useresti un documento qui con sed.
G-Man dice "Ripristina Monica" il

8

Usando GNU sed

Per sostituire le righe che iniziano una corrispondenza di riga 3e continuano con una corrispondenza di riga 5con New Code:

$ seq 8 | sed '/3/,/5/{/5/ s/.*/New Code/; t; d}'
1
2
New Code
6
7
8

Per le linee nell'intervallo /3/,/5/, testiamo per vedere se la linea corrisponde 5(nel senso che questa è l'ultima riga nel gruppo) e, in tal caso, facciamo la sostituzione da inserire New Code. Se è stata effettuata la sostituzione, il tcomando dice a sed di saltare alla fine dei comandi (nel qual caso New Codeviene stampato). In caso contrario, il dcomando dice a sed di eliminare la riga.

Tutte le altre linee sono stampate normalmente.

Per modificare il file in atto, è -ipossibile utilizzare l' opzione:

sed -i.bak '/3/,/5/{/5/ s/.*/New Code/; t; d}' file

Usando awk

Per fare lo stesso usando awk:

$ seq 8 | awk '/3/,/5/{if (!f)print "New Code"; f=1; next}; 1'
1
2
New Code
6
7
8

Il comando awk tratta in modo /3/,/5/speciale le linee nell'intervallo . Per le linee in quell'intervallo, testiamo per vedere se fè zero (cioè, se !fè vero) e, in tal caso, stampiamo New Codee impostiamo fsu 1 e quindi saltiamo il resto dei comandi e saltiamo alla nextlinea.

Per le linee al di fuori dell'intervallo /3/,/5/, non viene eseguito alcun salto e ciò 1causa la stampa della linea. Più in dettaglio, 1è una condizione. Poiché 1non è zero, la condizione restituisce true. Poiché nessuna azione è associata alla condizione, viene eseguita l'azione predefinita che è quella di stampare la linea. Quindi 1è una scorciatoia per la stampa della linea.

Per modificare un file sul posto, l' -i inplaceopzione può essere usata con GNU awk4.1 o successivo:

awk -i inplace '/3/,/5/{if (!f)print "New Code"; f=1; next} 1' file

freddo. Non sono bravo awk, potresti per favore spiegare un po 'la tua awkespressione
Rahul,

1
@Rahul Certo. Ho aggiunto una spiegazione del codice awk.
Giovanni 1024,

1
Grazie John Questo è esattamente quello che stavo cercando.
Gibuti

Molto semplice rimuovere il blocco, quindi aggiungerne uno nuovo all'ultima riga:sed '/3/,/4/d;/5/cNew Code' awk '/5/{print "New Code"}/3/,/5/{next}1'
Costas

1

Sulla base della discussione di cui sopra, mi viene in mente una soluzione usando ex

seq 15 > test1.txt
ex test1.txt << EOEX
/^7/,/^9/c
abcd
123
.xyz
hfr4
.
w!
q
EOEX
cat test1.txt

Quanto sopra dà

1
2
3
4
5
6
abcd
123
.xyz
hfr4
10
11
12
13
14
15

Grazie a g-man per avermi informato del comando change e darmi chiarimenti sul punto. Entrambi sede excondividono una sintassi quasi simile per il comando change (anche per il comando delete, il comando append, ecc.).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.