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 zsh
viene assunto come shell.
sed
Solo 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/
( re
rappresenta 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 re
se si re
verifica 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 re
su successive linee; in altre parole: questo non rileverà la prima occorrenza di una re
corrispondenza 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
.
sed
fornisce 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 sed
come BSD (macOS)sed
(funzionerà anche con GNU sed
):
Poiché 0,/re/
non può essere utilizzato e il modulo 1,/re/
non rileverà re
se 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 s
chiamata; in entrambi i casi, regex foo
viene riutilizzato implicitamente, permettendoci di non doverlo duplicare, il che rende il codice più breve e più gestibile.
POSIX sed
necessita di nuove righe effettive dopo determinate funzioni, come ad esempio il nome di un'etichetta o addirittura la sua omissione, come nel caso t
qui; La suddivisione strategica dello script in più -e
opzioni è un'alternativa all'utilizzo di -e
una nuova riga effettiva: termina ogni blocco di script dove normalmente dovrebbe andare una nuova riga.
1 s/foo/bar/
sostituisce solo foo
sulla prima riga, se presente. In tal caso, si t
ramifica alla fine dello script (salta i comandi rimanenti sulla riga). (La t
funzione si ramifica su un'etichetta solo se la s
chiamata 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 sed
s' 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 1
corrisponde 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 1
inizia l'intervallo), non è stato ancora applicato alcun regex, quindi//
non si riferisce a nulla.
Ad eccezione della sed
speciale 0,/re/
sintassi di GNU , qualsiasi intervallo che inizia con un numero di riga ne preclude effettivamente l'uso //
.