Trova e sostituisci nel file e sovrascrivi il file non funziona, svuota il file


604

Vorrei eseguire una ricerca e sostituire un file HTML tramite la riga di comando.

Il mio comando è simile al seguente:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html > index.html

Quando eseguo questo e guardo il file in seguito, è vuoto. Ha eliminato il contenuto del mio file.

Quando eseguo questo dopo aver ripristinato di nuovo il file:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

Il stdoutè il contenuto del file, e Trova e sostituisci è stato eseguito.

Perché sta succedendo?


13
Alternativa al Perl:perl -pi -w -e 's/STRING_TO_REPLACE/REPLACE_WITH/g;' index.html
Gjorgji Tashkovski,

molto relativo sedcomando per trovare una stringa e sostituire l'intera linea: stackoverflow.com/questions/11245144/...
cregox

Risposte:


917

Quando la shell vede > index.htmlnella riga di comando apre il file index.htmlper la scrittura , cancellando tutto il suo contenuto precedente.

Per risolvere questo problema è necessario passare l' -iopzione per sedrendere in linea le modifiche e creare un backup del file originale prima che apporti le modifiche sul posto:

sed -i.bak s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

Senza .bak il comando fallirà su alcune piattaforme, come Mac OSX.


20
Dire truncates the fileinvece che opens the fileprobabilmente lo rende più chiaro.
Mikel

12
Almeno sul mio Mac, il primo suggerimento non funziona ... se stai eseguendo la sostituzione sul posto di un file, devi specificare un'estensione. Puoi almeno passare un'estensione di lunghezza zero però: sed -i '' s / STRING_TO_REPLACE / STRING_TO_REPLACE_IT / g index.html
Tom Lianza,

5
per variabili sed -i.bak 's /' $ search '/' $ replace '/ g' index.html
Fatima Zohra,

33
su osx, usa una stringa vuota '' come parametro per -i, come:sed -i '' 's/blah/xx/g'
Pierre Houston,

4
ma qual è il tuo .bakdopo sed -i?
Patrizio Bertoni,

210

Un'alternativa, utile, modello è:

sed -e 'script script' index.html > index.html.tmp && mv index.html.tmp index.html

Questo ha più o meno lo stesso effetto, senza usare l' -iopzione, e inoltre significa che, se lo script sed non riesce per qualche motivo, il file di input non viene bloccato. Inoltre, se la modifica ha esito positivo, non è disponibile alcun file di backup. Questo tipo di linguaggio può essere utile in Makefile.

Molte sed hanno l' -iopzione, ma non tutte; il posix sed è uno che non lo fa. Se stai puntando alla portabilità, quindi, è meglio evitarlo.


9
+1 per nessun file di backup che si trova in giro e non ostruisce il file di input se la modifica non riesce. Ha funzionato perfettamente su Mac.
Mike Grace,

Ha funzionato perfettamente per me. Grazie! (su un Mac)
interessato il

1
Questo ha funzionato perfettamente per me su Ubuntu Server 14.04 sed -i ha continuato a azzerare il file.
Chris Giddings,

2
Miglioramento estremamente minore:... && mv index.html{.tmp,}
EdwardGarson

5
@EdwardGarson In effetti, è probabilmente quello che userei se lo scrivessi - sono d'accordo che è più ordinato - ma sh(se ricordo bene) non ha quell'espansione {...}. In un Makefile potresti usare shpiuttosto che bash, quindi se stai mirando alla portabilità (o posixness) allora dovrai evitare quella costruzione.
Norman Gray il

95
sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' index.html

Questa operazione sostituisce localmente il file index.html. Citando la stringa si evitano problemi con spazi bianchi nella query e nella sostituzione.


57

usa l'opzione -i di sed, ad es

sed -i bak -e s/STRING_TO_REPLACE/REPLACE_WITH/g index.html

Cosa significa questo? sed: -i non può essere usato con stdin
sheetal

2
Ricorda di racchiudere il tuo motivo tra virgolette se contiene spazi bianchi -'s/STRING_TO_REPLACE/REPLACE_WITH/g'
Doug Thompson,

@sheetal: -iesegue la modifica sul posto dei file , quindi non ha senso combinarlo con l' input stdin .
mklement0

Questo potrebbe funzionare su macOS, ma non su Arch Linux per me.
xdevs23,

Senza -e, la risposta accettata non funziona su MacOS, Catalina. Con -e funziona.
cwhiii,

18

Per modificare più file (e salvare un backup di ciascuno come * .bak):

perl -p -i -e "s/\|/x/g" *  

prenderà tutti i file nella directory e sostituirà |con x questo si chiama "Perl pie" (facile come una torta)


1
È bello vedere qualcuno disposto a guardare la dichiarazione del problema e non solo i tag. OP non ha specificato sedcome requisito, lo ha utilizzato solo come strumento già provato.
user7412956,

14

Dovresti provare a utilizzare l'opzione -iper la modifica sul posto.


6
sed -i.bak "s#https.*\.com#$pub_url#g" MyHTMLFile.html

Se hai un link da aggiungere, prova questo. Cerca l'URL come sopra (iniziando con https e finendo con.com qui) e sostituiscilo con una stringa URL. Ho usato una variabile $pub_urlqui. squi significa ricerca eg significa sostituzione globale.

Funziona !


6

Attenzione: questo è un metodo pericoloso! Abusa dei buffer di I / O in Linux e con specifiche opzioni di buffering riesce a funzionare su piccoli file. È una curiosità interessante.Ma non usarlo per una situazione reale!

Oltre -iall'opzione sed è possibile utilizzare l' teeutilità .

Da man:

leggere - leggere dallo standard input e scrivere nello standard output e nei file

Quindi, la soluzione sarebbe:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee | tee index.html

- qui teeviene ripetuto per assicurarsi che la pipeline sia bufferizzata. Quindi tutti i comandi nella pipeline vengono bloccati fino a quando non ottengono un input su cui lavorare. Ogni comando nella pipeline inizia quando i comandi upstream hanno scritto 1 buffer di byte (la dimensione è definita da qualche parte ) nell'input del comando. Quindi l'ultimo comandotee index.html , che apre il file per la scrittura e quindi lo svuota, viene eseguito al termine della pipeline upstream e l'output si trova nel buffer all'interno della pipeline.

Molto probabilmente il seguente non funzionerà:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee index.html

- eseguirà entrambi i comandi della pipeline contemporaneamente senza alcun blocco. (Senza bloccare il gasdotto dovrebbe passare la linea byte per riga invece di buffer tampone. Come quando si esegue cat | sed s/bar/GGG/. Senza blocco è più interattivo e solitamente condutture di soli 2 comandi eseguito senza buffering e bloccare. Tubazioni più lunghe sono bufferizzati.) La tee index.htmlvolontà aprire il file per la scrittura e verrà svuotato. Tuttavia, se si attiva sempre il buffering, anche la seconda versione funzionerà.


3
Il file di output di tee viene inoltre aperto immediatamente con un indice.html vuoto per l'intero comando.
sabato

3
Ciò danneggerà qualsiasi file di input che è più grande del buffer della pipeline (che in genere è 64 KB) . (@sjngm: il file non viene troncato all'istante come con >, ma il punto è che si tratta di una soluzione rotta che potrebbe causare la perdita di dati).
mklement0,

4

Il problema con il comando

sed 'code' file > file

è che fileviene troncato dalla shell prima che sed riesca effettivamente a elaborarla. Di conseguenza, si ottiene un file vuoto.

Il modo sed per farlo è usare -iper modificare sul posto, come suggerito da altre risposte. Tuttavia, questo non è sempre quello che vuoi. -icreerà un file temporaneo che verrà quindi utilizzato per sostituire il file originale. Ciò è problematico se il file originale era un collegamento (il collegamento verrà sostituito da un file normale). Se è necessario conservare i collegamenti, è possibile utilizzare una variabile temporanea per memorizzare l'output di sed prima di riscriverlo nel file, in questo modo:

tmp=$(sed 'code' file); echo -n "$tmp" > file

Meglio ancora, usare al printfposto di echopoiché echoè probabile che \\funzioni come \in alcune shell (ad esempio trattino):

tmp=$(sed 'code' file); printf "%s" "$tmp" > file

1
+1 per conservare i collegamenti. Funziona anche con un file temporaneo:sed 'code' file > file.tmp; cat file.tmp > file; rm file.tmp
dashohoxha,

3

E la edrisposta:

printf "%s\n" '1,$s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' w q | ed index.html

Per ribadire la risposta a codaddict , la shell gestisce prima il reindirizzamento , cancellando il file "input.html", quindi la shell invoca il comando "sed" passandogli un file ora vuoto.


2
domanda veloce, perché le persone continuano a dare "la edversione" delle sedrisposte? si esibisce più velocemente?
Cregox,

6
Alcuni seds non implementano -iper modificare sul posto. edè onnipresente e ti consente di salvare le modifiche nel file originale. Inoltre è sempre utile avere molti strumenti nel kit.
Glenn Jackman,

ok bello. quindi, per quanto riguarda le prestazioni, suppongo che siano le stesse. Grazie!
Cregox,

2

Puoi usare Vim in modalità Ex:

ex -sc '%s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g|x' index.html
  1. % seleziona tutte le righe

  2. x salva e chiudi


0

Stavo cercando l'opzione in cui posso definire l'intervallo di linee e ho trovato la risposta. Ad esempio, voglio cambiare host1 in host2 dalla riga 36-57.

sed '36,57 s/host1/host2/g' myfile.txt > myfile1.txt

Puoi usare anche l'opzione gi per ignorare il caso del personaggio.

sed '30,40 s/version/story/gi' myfile.txt > myfile1.txt

0

Con tutto il dovuto rispetto per le risposte esatte sopra, è sempre una buona idea "script di esecuzione a secco" in questo modo, in modo da non danneggiare il file e ricominciare da zero.

Basta che il tuo script sparga l'output alla riga di comando invece di scriverlo nel file, ad esempio in questo modo:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

O

less index.html | sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g 

In questo modo è possibile visualizzare e controllare l'output del comando senza troncare il file.

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.