Una panoramica delle molte utili risposte esistenti , integrate da spiegazioni :
Gli esempi qui usano un caso d'uso semplificato: sostituisci la parola "pippo" con "barra" solo nella prima riga corrispondente.
Grazie all'uso di stringhe ANSI C-quotati ( $'...') per fornire le linee di ingresso del campione, bash, ksh, o zshviene assunto come shell.
sedSolo GNU :
La risposta di Ben Hoffstein ci mostra che GNU fornisce un'estensione alla specifica POSIXsed che consente il seguente modulo a 2 indirizzi : 0,/re/( rerappresenta qui un'espressione regolare arbitraria).
0,/re/consente alla regex di corrispondere anche sulla prima riga . In altre parole: un tale indirizzo creerà un intervallo dalla 1a riga fino alla riga corrispondente, compresa rese si reverifica sulla 1a riga o su qualsiasi riga successiva.
- In contrasto con la forma POSIX-compliant
1,/re/, che crea una gamma che partite dal 1 ° linea fino ad includere la linea che le partite resu successive linee; in altre parole: questo non rileverà la prima occorrenza di una recorrispondenza se si verifica sulla prima riga e impedisce// anche l'uso della scorciatoia per il riutilizzo dell'ultima regex utilizzata (vedere il punto successivo). 1
Se si combina un 0,/re/indirizzo con una s/.../.../chiamata (sostituzione) che utilizza la stessa espressione regolare, il comando eseguirà effettivamente la sostituzione solo sulla prima riga corrispondente re.
sedfornisce una comoda scorciatoia per riutilizzare l'espressione regolare più recentemente applicato : un vuoto coppia delimitatore// .
$ sed '0,/foo/ s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo
Solo funzionalità POSIX sedcome BSD (macOS)sed (funzionerà anche con GNU sed ):
Poiché 0,/re/non può essere utilizzato e il modulo 1,/re/non rileverà rese si verifica nella prima riga (vedere sopra), è richiesta una gestione speciale per la prima riga .
La risposta di MikhailVS menziona la tecnica, messa qui in un esempio concreto:
$ sed -e '1 s/foo/bar/; t' -e '1,// s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo
Nota:
Il //collegamento regex vuoto viene impiegato due volte qui: una volta per l'endpoint dell'intervallo e una volta nella schiamata; in entrambi i casi, regex fooviene riutilizzato implicitamente, permettendoci di non doverlo duplicare, il che rende il codice più breve e più gestibile.
POSIX sednecessita di nuove righe effettive dopo determinate funzioni, come ad esempio il nome di un'etichetta o addirittura la sua omissione, come nel caso tqui; La suddivisione strategica dello script in più -eopzioni è un'alternativa all'utilizzo di -euna nuova riga effettiva: termina ogni blocco di script dove normalmente dovrebbe andare una nuova riga.
1 s/foo/bar/sostituisce solo foosulla prima riga, se presente. In tal caso, si tramifica alla fine dello script (salta i comandi rimanenti sulla riga). (La tfunzione si ramifica su un'etichetta solo se la schiamata più recente ha eseguito una sostituzione effettiva; in assenza di un'etichetta, come nel caso qui, la fine dello script è ramificata a).
Quando ciò accade, l'indirizzo di intervallo 1,//, che normalmente trova la prima occorrenza a partire dalla riga 2 , non corrisponderà e l'intervallo non verrà elaborato, poiché l'indirizzo viene valutato quando la riga corrente è già 2.
Viceversa, se non c'è corrispondenza sulla prima riga, 1,// verrà inserito e troverà la prima vera corrispondenza.
L'effetto netto è la stessa con GNU seds' 0,/re/: solo la prima occorrenza è sostituita, se si verifica sulla linea 1 o qualsiasi altro.
Approcci non di portata
la risposta di potong dimostra tecniche di loop che aggirano la necessità di un intervallo ; poiché usa la sintassi GNU sed , ecco gli equivalenti compatibili con POSIX :
Tecnica del loop 1: al primo incontro, esegui la sostituzione, quindi inserisci un loop che stampa semplicemente le righe rimanenti così come sono :
$ sed -e '/foo/ {s//bar/; ' -e ':a' -e '$!{n;ba' -e '};}' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo
Loop tecnica 2, solo per file di piccole dimensioni : leggi l'intero input in memoria, quindi esegui una singola sostituzione su di esso .
$ sed -e ':a' -e '$!{N;ba' -e '}; s/foo/bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo
1 1.61803 fornisce esempi di ciò che accade 1,/re/, con e senza un successivo s//:
- sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'rendimenti $'1bar\n2bar'; cioè, entrambe le linee sono state aggiornate, perché il numero di riga 1corrisponde alla prima riga e regex /foo/- la fine dell'intervallo - viene quindi cercato solo a partire dalla riga successiva . Pertanto, entrambe le linee vengono selezionate in questo caso e la s/foo/bar/sostituzione viene eseguita su entrambe.
- sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo' fallisce : con sed: first RE may not be empty(BSD / macOS) e sed: -e expression #1, char 0: no previous regular expression(GNU), perché, al momento dell'elaborazione della prima riga (a causa del numero di riga che 1inizia l'intervallo), non è stato ancora applicato alcun regex, quindi//non si riferisce a nulla.
Ad eccezione della sedspeciale 0,/re/sintassi di GNU , qualsiasi intervallo che inizia con un numero di riga ne preclude effettivamente l'uso //.