Come grep e sostituire


252

Devo cercare ricorsivamente una stringa specificata all'interno di tutti i file e sottodirectory all'interno di una directory e sostituire questa stringa con un'altra stringa.

So che il comando per trovarlo potrebbe assomigliare a questo:

grep 'string_to_find' -r ./*

Ma come posso sostituire ogni istanza di string_to_findcon un'altra stringa?


Non credo che grep possa farlo (potrei sbagliarmi). Modi più semplici sarebbero usare sed o perl per fare il rimpiazzo
Memento Mori

2
Prova ad usaresed -i 's/.*substring.*/replace/'
Eddy_Em

2
@Eddy_Em Sostituirà l'intera riga con sostituisci. È necessario utilizzare il raggruppamento per acquisire la parte della linea prima e dopo la sottostringa e quindi inserirla nella linea di sostituzione. sed -i 's/\(.*\)substring\(.*\)/\1replace\2/'
JStrahl,


Risposte:


249

Un'altra opzione è quella di usare find e poi passarlo attraverso sed.

find /path/to/files -type f -exec sed -i 's/oldstring/new string/g' {} \;

34
Sul terminale OS X 10.10, -iè richiesta una stringa di estensione appropriata per il parametro . Ad esempio, find /path/to/files -type f -exec sed -i "" "s/oldstring/new string/g" {} \;fornendo una stringa vuota crea comunque un file di backup a differenza di quanto descritto nel manuale ...
Eonil

10
Perché visualizzo "errore sed: RE: sequenza di byte non valida". E sì, ho aggiunto -i ""OS X. Funziona diversamente.
Taco,

2
Ho avuto il problema della sequenza di byte illegale su macOS 10.12 e questa domanda / risposta ha risolto il mio problema: stackoverflow.com/questions/19242275/… .
abeboparebop,

3
Questo tocca ogni file così i tempi dei file vengono modificati; e converte le terminazioni di linea da CRLFa LFsu Windows.
17-17

183

Ho la risposta

grep -rl matchstring somedir/ | xargs sed -i 's/string1/string2/g'

14
Questo eseguirà la scansione dei file corrispondenti due volte ... una volta con grepe poi di nuovo con sed. L'uso del findmetodo è più efficiente ma questo metodo che menzioni funziona.
cmevoli

41
Su OS X è necessario cambiare sed -i 's/str1/str2/g'per sed -i "" 's/str1/str2/g'questo al lavoro.
jdf,

6
@cmevoli con questo metodo, greppassa attraverso tutti i file e sedscansiona solo i file corrispondenti grep. Con il findmetodo nell'altra risposta, findprima elenca tutti i file, quindi sedeseguirà la scansione di tutti i file in quella directory. Quindi questo metodo non è necessariamente più lento, dipende da quante partite ci sono e le differenze di velocità di ricerca tra sed, grepe find.
Joelostblom,

4
OTOH in questo modo ti consente di ANTEPRIMA che cosa trova grep PRIMA di sostituire effettivamente, riducendo notevolmente il rischio di fallimento, specialmente per regex n00bs come me
Lennart Rolland,

2
Questo è utile anche quando la sostituzione di grep è più intelligente di sed. Per esempio ripgrep obbedisce a .gitignore mentre sed no.
user31389

43

Potresti persino farlo in questo modo:

Esempio

grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'

Questo cercherà la stringa ' windows ' in tutti i file relativi alla directory corrente e sostituirà ' windows ' con ' linux ' per ogni occorrenza della stringa in ciascun file.


2
È greputile solo se ci sono file che non devono essere modificati. L'esecuzione sedsu tutti i file aggiornerà la data di modifica del file ma lascerà invariato il contenuto se non ci sono corrispondenze.
Tripleee,

@tripleee: stai attento con ... ma [sed] lascia invariato il contenuto se non ci sono corrispondenze " . Durante l'utilizzo -i, credo che sedcambi il tempo del file di ogni file che tocca, anche se il contenuto è invariato. sedConverte anche le terminazioni di riga Non uso sedsu Windows in un repository Git perché tutto CRLFè cambiato in LF.
jww

Questo comando ha bisogno di un "" dopo -i per indicare che non verranno creati file di backup dopo la sostituzione sul posto, almeno in macosx. Controlla la pagina man per i dettagli. Se si desidera un backup, è qui che si inserisce l'estensione del file da creare.
spinyBabbler

31

Questo funziona meglio per me su OS X:

grep -r -l 'searchtext' . | sort | uniq | xargs perl -e "s/matchtext/replacetext/" -pi

Fonte: http://www.praj.com.au/post/23691181208/grep-replace-text-string-in-files


questo è perfetto! funziona anche con ag:ag "search" -l -r . | sort | uniq | xargs perl -e 's/search/replace' -pi

@sebastiankeller Il tuo comando Perl manca della barra finale, che è un errore di sintassi.
Tripleee,

3
Perché la sort -uparte pari di questo? In quali circostanze ti aspetti grep -rldi produrre due volte lo stesso nome file?
Tripleee

5

Altre soluzioni mescolano sintassi regex. Per utilizzare i modelli perl / PCRE sia per la ricerca che per la sostituzione e per elaborare solo i file corrispondenti, funziona abbastanza bene:

grep -rlZPi 'match1' | xargs -0r perl -pi -e 's/match2/replace/gi;'

dove match1e match2sono generalmente identici ma match1possono essere semplificati per rimuovere funzioni più avanzate che sono rilevanti solo per la sostituzione, ad esempio gruppi di acquisizione.

Traduzione: grepricorsivamente ed elenca i file che corrispondono a questo modello PCRE, separati da nul per proteggere eventuali caratteri speciali nel nome del file, quindi reindirizza quei nomi di file a xargscui si aspetta un elenco nul-separato, ma non farà nulla se non viene ricevuto alcun nome, e arriva perla sostituire le righe in cui si trovano le corrispondenze.

Aggiungi l' Iopzione a grepper ignorare i file binari. Per la corrispondenza con distinzione tra maiuscole e minuscole, rilasciare l' iinterruttore da grepe il iflag attaccato all'espressione di sostituzione, ma non l' iinterruttore su perlse stesso.


Perl stesso è abbastanza in grado di ricorrere a una struttura di file. In effetti, esiste uno strumento find2perlfornito con Perl che fa questo genere di cose senza alcun xargstrucco.
Tripleee

@tripleee findnon cerca i contenuti dei file e il punto è elaborare solo i file corrispondenti senza scrivere un programma Perl.
Walf,

Questa è una buona soluzione per Windows, in quanto evita il problema delle soluzioni sed-based di convertire i finali di linea. Grazie!
JamHandy,

4

Di solito non con grep, ma piuttosto con sed -i 's/string_to_find/another_string/g'o perl -i.bak -pe 's/string_to_find/another_string/g'.


3

Fai molta attenzione quando usi finde sedin un repository git! Se non escludi i file binari puoi finire con questo errore:

error: bad index file sha1 signature 
fatal: index file corrupt

Per risolvere questo errore è necessario ripristinare il sedsostituendo il tuo new_stringcon il tuo old_string. Ciò ripristinerà le stringhe sostituite, quindi tornerai all'inizio del problema.

Il modo corretto di cercare una stringa e sostituirla è saltare finde usare grepinvece per ignorare i file binari:

sed -ri -e "s/old_string/new_string/g" $(grep -Elr --binary-files=without-match "old_string" "/files_dir")

Crediti per @hobs


1

Ecco cosa farei:

find /path/to/dir -type f -iname "*filename*" -print0 | xargs -0 sed -i '/searchstring/s/old/new/g'

questo cercherà tutti i file che contengono filenameil nome del file sotto /path/to/dir, che per ogni file trovato, cerca la linea con searchstringe sostituisce oldcon new.

Tuttavia, se si desidera omettere la ricerca di un file specifico con una filenamestringa nel nome del file, è sufficiente:

find /path/to/dir -type f -print0 | xargs -0 sed -i '/searchstring/s/old/new/g'

Questo farà la stessa cosa sopra, ma per tutti i file trovati sotto /path/to/dir.


0

Un'altra opzione sarebbe quella di usare solo perl con globstar.

L'abilitazione shopt -s globstarnel tuo .bashrc(o ovunque) consente al **modello glob di abbinare ricorsivamente tutte le sottodirectory e i file.

Quindi l'utilizzo perl -pXe 's/SEARCH/REPLACE/g' -i **sostituirà ricorsivamente SEARCHcon REPLACE.

La -Xbandiera dice a perl di "disabilitare tutti gli avvisi", il che significa che non si lamenterà delle directory.

La globstar ti consente anche di fare cose come sed -i 's/SEARCH/REPLACE/g' **/*.extse volessi sostituirle SEARCHcon REPLACEtutti i file secondari con l'estensione .ext.


"Un'altra opzione sarebbe quella di usare solo perl con globstar ..." - Non su macchine Posixy, come Solaris. Ecco perché sto specificatamente cercando grepe sed.
17-17
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.