Inserisci la riga dopo il primo incontro usando sed


245

Per qualche motivo non riesco a trovare una risposta semplice a questo e al momento sono un po 'in crisi. Come potrei fare per l'inserimento di una linea di scelta del testo dopo la prima riga corrispondente una stringa specifica utilizzando il sedcomando. Io ho ...

CLIENTSCRIPT="foo"
CLIENTFILE="bar"

E voglio inserire una riga dopo la CLIENTSCRIPT=riga risultante in ...

CLIENTSCRIPT="foo"
CLIENTSCRIPT2="hello"
CLIENTFILE="bar"

Risposte:


379

Prova a farlo usando GNU sed:

sed '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' file

se si desidera sostituire sul posto , utilizzare

sed -i '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' file

Produzione

CLIENTSCRIPT="foo"
CLIENTSCRIPT2="hello"
CLIENTFILE="bar"

Doc


Grazie! E per farlo riscrivere nel file senza stampare il contenuto del file?
user2150250

11
Si noti che entrambi assumono GNU sed. Questa non è sedsintassi standard e non funzionerà con nessun'altra sedimplementazione.
Stephane Chazelas,

Ho avuto problemi a farlo funzionare per me perché stavo usando un delimitatore diverso. Dopo RTFM, mi sono reso conto che dovevo iniziare la regex con un \, quindi il delimitatore.
Christy,

10
Come si applica SOLO alla prima partita? è chiaro che aggiunge il testo dopo la partita, ma come fa a sapere solo alla prima partita?
Mohammed Noureldin,

7
Questo aggiunge il testo dopo ogni partita, per me.
schoppenhauer,

49

Nota la sedsintassi standard (come in POSIX, così supportata da tutte le sedimplementazioni conformi in circolazione (GNU, OS / X, BSD, Solaris ...)):

sed '/CLIENTSCRIPT=/a\
CLIENTSCRIPT2="hello"' file

O su una riga:

sed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' file

( -expressions (e il contenuto di -files) sono uniti a nuove righe per comporre gli sedinterpreti di script sed ).

L' -iopzione per la modifica sul posto è anche un'estensione GNU, alcune altre implementazioni (come quella di FreeBSD) supportano -i ''questo.

In alternativa, per la portabilità, puoi usare perlinvece:

perl -pi -e '$_ .= qq(CLIENTSCRIPT2="hello"\n) if /CLIENTSCRIPT=/' file

Oppure puoi usare edo ex:

printf '%s\n' /CLIENTSCRIPT=/a 'CLIENTSCRIPT2="hello"' . w q | ex -s file

2
Potrei sbagliarmi, ma la corrente sed -e '/CLIENTSCRIPT=/a\' -e 'CLIENTSCRIPT2="hello"' filesfugge alla citazione alla fine del primo parametro e interrompe il comando.
Carrello abbandonato

1
@AbandonedCart, in gusci di Bourne, csho rcfamiglia, '...'sono virgolette forti all'interno delle quali la barra rovesciata non è speciale. L'unica eccezione che conosco è la fishshell.
Stephane Chazelas,

1
Anche il terminale su MacOS (e successivamente uno script eseguito nel terminale) è un'eccezione. Ho trovato sintassi alternativa, ma grazie comunque.
Carrello abbandonato

1
@AbandonedCart, questo è qualcos'altro. MacOS sednon è conforme a POSIX qui. Ciò rende errata la mia affermazione sul fatto che sia portatile (per la variante a una riga). Chiederò conferma al gruppo operativo se si tratta effettivamente di una non conformità o di una cattiva interpretazione dello standard da parte mia.
Stephane Chazelas,

19

Uno conforme a POSIX usando il scomando:

sed '/CLIENTSCRIPT="foo"/s/.*/&\
CLIENTSCRIPT2="hello"/' file

1
... che supporta anche l'inserimento di nuove linee. Lo adoro!
Stephan Henningsen,

1
Vedi anche sed -e '/.../s/$/\' -e 'CLI.../'che eviterebbe problemi con le linee che contengono sequenze di byte che non formano caratteri validi.
Stephane Chazelas,

14

Comando Sed che funziona su MacOS (almeno, OS 10) e Unix (cioè non richiede gnu sed come Gilles (attualmente accettato)):

sed -e '/CLIENTSCRIPT="foo"/a\'$'\n''CLIENTSCRIPT2="hello"' file

Funziona in bash e forse anche in altre shell che conoscono lo stile di quotazione di valutazione $ '\ n'. Tutto può essere su una riga e funzionare con i comandi sed / POSIX precedenti. Se potrebbero esserci più righe corrispondenti a CLIENTSCRIPT = "foo" (o il tuo equivalente) e desideri aggiungere la riga aggiuntiva solo la prima volta, puoi rielaborarla come segue:

sed -e '/^ *CLIENTSCRIPT="foo"/b ins' -e b -e ':ins' -e 'a\'$'\n''CLIENTSCRIPT2="hello"' -e ': done' -e 'n;b done' file

(questo crea un ciclo dopo il codice di inserimento della riga che scorre semplicemente attraverso il resto del file, non tornando mai più al primo comando sed).

Potresti notare che ho aggiunto un '^ *' al modello di corrispondenza nel caso in cui quella riga venga visualizzata in un commento, diciamo, o sia rientrata. Non è perfetto al 100% ma copre alcune altre situazioni che potrebbero essere comuni. Regola come richiesto ...

Queste due soluzioni risolvono anche il problema (per la soluzione generica di aggiungere una linea) che se la tua nuova linea inserita contiene barre rovesciate o e commerciali senza caratteri di escape, saranno interpretate da sed e probabilmente non usciranno allo stesso modo, proprio come lo \nè - ad es. \0sarebbe la prima riga abbinata. Particolarmente utile se stai aggiungendo una riga che proviene da una variabile in cui altrimenti dovresti sfuggire a tutto prima usando $ {var //} o un'altra istruzione sed ecc.

Questa soluzione è un po 'meno complicata negli script (che la quotazione e \ n non è facile da leggere però), quando non si desidera inserire il testo sostitutivo per il comando a all'inizio di una riga, se si dice, in una funzione con linee rientrate. Ho approfittato del fatto che $ '\ n' viene valutato dalla shell come una nuova riga, non è in normali '\ n' valori tra virgolette singole.

Sta diventando abbastanza lungo anche se penso che perl / perfino awk potrebbe vincere a causa della maggiore leggibilità.


1
Grazie per la risposta! Il primo funziona come un incantesimo anche sul sistema operativo AIX.
abhishek,

come si fa ma per l'inserimento su più righe?
Tatsu,

1
POSIX sed supporta letteralmente nuove righe nella stringa di sostituzione se le "scappi" con una barra rovesciata ( sorgente ). Quindi: s/CLIENTSCRIPT="foo"/&\ [Invio] CLIENTSCRIPT2="hello"/ . La barra rovesciata deve essere l'ultimo carattere della riga (nessuno spazio dopo), contrariamente a come appare in questo commento.
TheDudeAbides

1
@tatsu Newline nella stringa di sostituzione di s///, così come quelli nei comandi ae ipossono essere "sfuggiti", usando lo stesso metodo che descrivo nel mio commento sopra.
TheDudeAbides

4

Forse un po 'in ritardo per pubblicare una risposta per questo, ma ho trovato alcune delle soluzioni di cui sopra un po' ingombranti.

Ho provato la semplice sostituzione della stringa in sed e ha funzionato:

sed 's/CLIENTSCRIPT="foo"/&\nCLIENTSCRIPT2="hello"/' file

& sign riflette la stringa corrispondente, quindi aggiungi \ n e la nuova riga.

Come accennato, se si desidera farlo sul posto:

sed -i 's/CLIENTSCRIPT="foo"/&\nCLIENTSCRIPT2="hello"/' file

Un'altra cosa. Puoi abbinare usando un'espressione:

sed -i 's/CLIENTSCRIPT=.*/&\nCLIENTSCRIPT2="hello"/' file

Spero che questo aiuti qualcuno


Funziona bene su Linux, dove GNU sed è predefinito, perché comprende \nnella stringa di sostituzione. Plain vanilla (POSIX) sednon non comprendere \nnella stringa di sostituzione, tuttavia, quindi questo non funziona su BSD o MacOS-a meno che non avete installato GNU sed in qualche modo. Con vanilla sed puoi incorporare nuove linee "sfuggendole", ovvero terminare la linea con una barra rovesciata, quindi premere [Invio] ( riferimento , sotto l'intestazione per [2addr]s/BRE/replacement/flags). Ciò significa che il comando sed deve estendersi su più righe nel terminale.
TheDudeAbides

Un'altra opzione per ottenere una nuova riga letterale nella stringa di sostituzione è utilizzare la funzione di quotazione ANSI-C in Bash, come menzionato in una delle altre risposte .
TheDudeAbides


1

Avevo un compito simile e non ero in grado di far funzionare la soluzione perl di cui sopra.

Ecco la mia soluzione:

perl -i -pe "BEGIN{undef $/;} s/^\[mysqld\]$/[mysqld]\n\ncollation-server = utf8_unicode_ci\n/sgm" /etc/mysql/my.cnf

Spiegazione:

Utilizza un'espressione regolare per cercare una riga nel mio file /etc/mysql/my.cnf che lo conteneva solo [mysqld]e lo ha sostituito con

[mysqld] collation-server = utf8_unicode_ci

l'aggiunta efficace della collation-server = utf8_unicode_ciriga dopo la riga contenente [mysqld].


0

Ho dovuto farlo di recente anche per i sistemi operativi Mac e Linux e dopo aver sfogliato molti post e provato molte cose, secondo la mia opinione particolare non sono mai arrivato dove volevo che fosse: una soluzione abbastanza semplice da capire usando un noto e comandi standard con schemi semplici, un liner, portatile, espandibile per aggiungere ulteriori vincoli. Poi ho provato a guardarlo con una prospettiva diversa, in quel momento mi sono reso conto che avrei potuto fare a meno dell'opzione "un liner" se un "2 liner" soddisfacesse il resto dei miei criteri. Alla fine mi è venuta in mente questa soluzione che mi piace che funzioni sia su Ubuntu che su Mac che volevo condividere con tutti:

insertLine=$(( $(grep -n "foo" sample.txt | cut -f1 -d: | head -1) + 1 ))
sed -i -e "$insertLine"' i\'$'\n''bar'$'\n' sample.txt

Nel primo comando, grep cerca i numeri di riga contenenti "pippo", cut / head seleziona la prima occorrenza e l'operazione aritmetica incrementa il numero di riga della prima occorrenza di 1 poiché voglio inserire dopo la ricorrenza. Nel secondo comando, è una modifica del file sul posto, "i" per l'inserimento: un ansi-c che cita una nuova riga, "barra", quindi un'altra nuova riga. Il risultato è l'aggiunta di una nuova riga contenente "barra" dopo la riga "pippo". Ognuno di questi 2 comandi può essere espanso in operazioni e abbinamento più complessi.

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.