sed modifica file sul posto

Sto cercando di scoprire se è ansible modificare un file in un singolo comando sed senza scorrere manualmente il contenuto modificato in un nuovo file e quindi rinominare il nuovo file con il nome del file originale. Ho provato l’opzione -i ma il mio sistema Solaris ha detto che -i è un’opzione illegale. C’è un modo diverso?

L’ opzione -i trasmette il contenuto modificato in un nuovo file e poi lo rinomina dietro le quinte, comunque.

Esempio:

 sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename 

e

 sed -i '' 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename 

su macOS .

Su un sistema in cui sed non ha la possibilità di modificare i file sul posto, penso che la soluzione migliore sarebbe usare perl :

 perl -pi -e 's/foo/bar/g' file.txt 

Sebbene questo crei un file temporaneo, sostituisce l’originale perché è stato fornito un suffisso / estensione vuoto.

Nota che su OS X potresti ricevere strani errori come “codice di comando non valido” o altri strani errori durante l’esecuzione di questo comando. Per risolvere questo problema, prova

 sed -i '' -e "s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g"  

Questo perché nella versione OSX di sed , l’opzione -i prevede un argomento di extension modo che il comando venga effettivamente analizzato come argomento di extension e il percorso del file sia interpretato come codice di comando. Fonte: https://stackoverflow.com/a/19457213

Quanto segue funziona bene sul mio mac

 sed -i.bak 's/foo/bar/g' sample 

Stiamo sostituendo foo con bar nel file di esempio. Il backup del file originale verrà salvato in sample.bak

Per modificare in linea senza backup, utilizzare il seguente comando

 sed -i'' 's/foo/bar/g' sample 

Una cosa da notare, sed non può scrivere file da sola in quanto l’unico scopo di sed è quello di agire come un editor sul “stream” (cioè pipeline di stdin, stdout, stderr e altri >&n buffer, socket e simili) . Tenendo presente questo, è ansible utilizzare un altro comando tee per scrivere l’output sul file. Un’altra opzione è creare una patch dal piping del contenuto in diff .

Metodo Tee

sed '/regex/' | tee

Metodo di patch

sed '/regex/' | diff -p /dev/stdin | patch

AGGIORNARE:

Inoltre, nota che patch farà cambiare il file dalla riga 1 dell’output diff:

Patch non ha bisogno di sapere quale file accedere come questo si trova nella prima riga dell’output da diff:

 $ echo foobar | tee fubar $ sed 's/oo/u/' fubar | diff -p fubar /dev/stdin *** fubar 2014-03-15 18:06:09.000000000 -0500 --- /dev/stdin 2014-03-15 18:06:41.000000000 -0500 *************** *** 1 **** ! foobar --- 1 ---- ! fubar $ sed 's/oo/u/' fubar | diff -p fubar /dev/stdin | patch patching file fubar 

Non è ansible fare ciò che vuoi con sed . Anche le versioni di sed che supportano l’opzione -i per la modifica di un file sul posto eseguono esattamente ciò che hai dichiarato esplicitamente che non vuoi: scrivono in un file temporaneo e quindi rinominano il file. Ma forse puoi semplicemente usare ed . Ad esempio, per modificare tutte le occorrenze di foo in bar nel file file.txt , puoi eseguire:

 echo ',s/foo/bar/g; w' | tr \; '\012' | ed -s file.txt 

La syntax è simile a sed , ma certamente non esattamente la stessa.

Ma forse dovresti considerare il motivo per cui non vuoi usare un file temporaneo rinominato. Anche se non hai un supporto -i supporta sed , puoi facilmente scrivere una sceneggiatura per fare il lavoro per te. Invece del file sed -i 's/foo/bar/g' file , potresti fare inline file sed 's/foo/bar/g' . Tale script è banale da scrivere. Per esempio:

 #!/bin/sh -e IN=$1 shift trap 'rm -f $tmp' 0 tmp=$( mktemp ) <$IN "[email protected]" >$tmp && cat $tmp > $IN # preserve hard links 

dovrebbe essere adeguato per la maggior parte degli usi.

sed supporta la modifica sul posto. Da man sed :

 -i[SUFFIX], --in-place[=SUFFIX] edit files in place (makes backup if extension supplied) 

Esempio :

Supponiamo che tu abbia un file hello.txt con il testo:

 hello world! 

Se vuoi conservare un backup del vecchio file, usa:

 sed -i.bak 's/hello/bonjour' hello.txt 

hello.txt con due file: hello.txt con il contenuto:

 bonjour world! 

e hello.txt.bak con il vecchio contenuto.

Se non vuoi conservare una copia, non passare il parametro di estensione.

Potresti usare vi

 vi -c '%s/foo/bar/g' my.txt -c 'wq' 
 mv file.txt file.tmp && sed 's/foo/bar/g' < file.tmp > file.txt 

Dovrebbe preservare tutti gli hardlink, poiché l’output viene reindirizzato per sovrascrivere il contenuto del file originale ed evita qualsiasi necessità di una versione speciale di sed.

Se stai sostituendo la stessa quantità di caratteri e dopo aver letto attentamente la modifica “sul posto” dei file …

Puoi anche usare l’operatore di reindirizzamento <> per aprire il file da leggere e scrivere:

 sed 's/foo/bar/g' file 1<> file 

Guardalo dal vivo:

 $ cat file hello i am here # see "here" $ sed 's/here/away/' file 1<> file # Run the `sed` command $ cat file hello i am away # this line is changed now 

Da Bash Manuale di riferimento → 3.6.10 Apertura dei descrittori di file per la lettura e la scrittura :

L’operatore di reindirizzamento

 [n]<>word 

causa il file il cui nome è l’espansione della parola da aprire sia per la lettura che per la scrittura sul descrittore di file n o sul descrittore di file 0 se n non è specificato. Se il file non esiste, viene creato.

Non hai specificato quale shell stai usando, ma con zsh potresti usare il costrutto =( ) per ottenere ciò. Qualcosa sulla falsariga di:

 cp =(sed ... file; sync) file 

=( ) è simile a >( ) ma crea un file temporaneo che viene automaticamente cancellato quando cp termina.

Come ha detto Moneypenny in Skyfall: “A volte i vecchi modi sono i migliori.” Kincade ha detto qualcosa di simile più tardi.

$ printf ‘, s / false / true / g \ nw \ n’ | ed {YourFileHere}

Buon assembly sul posto. Aggiunto ‘\ nw \ n’ per scrivere il file. Ci scusiamo per la richiesta di risposta al ritardo.

Ottimi esempi Ho avuto la sfida di modificare molti file e l’opzione -i sembra essere l’unica soluzione ragionevole utilizzandola nel comando find. Qui lo script per aggiungere “versione:” davanti alla prima riga di ogni file:

 find . -name pkg.json -print -exec sed -i '.bak' '1 s/^/version /' {} \;