La sostituzione delle stringhe nei file in base a determinati criteri di ricerca è un'operazione molto comune. Come posso
- sostituisci la stringa
fooconbarin tutti i file nella directory corrente? - Fare lo stesso ricorsivamente per le sottodirectory?
- sostituire solo se il nome del file corrisponde a un'altra stringa?
- sostituire solo se la stringa si trova in un determinato contesto?
- sostituire se la stringa si trova su un certo numero di riga?
- sostituisci più stringhe con la stessa sostituzione
- sostituisci più stringhe con sostituzioni diverse
Risposta accettata:
1. Sostituzione di tutte le occorrenze di una stringa con un'altra in tutti i file nella directory corrente:
Questi sono per i casi in cui sai che la directory contenga solo file regolari e che desideri elaborare tutti i file non nascosti. In caso contrario, utilizzare gli approcci in 2.
Tutti i sed le soluzioni in questa risposta presuppongono GNU sed . Se utilizzi FreeBSD o macOS, sostituisci -i con -i '' . Si noti inoltre che l'uso di -i passare con qualsiasi versione di sed ha alcune implicazioni sulla sicurezza del filesystem ed è sconsigliabile in qualsiasi script che intendi distribuire in alcun modo.
-
Non ricorsivo, solo file in questa directory:
sed -i -- 's/foo/bar/g' * perl -i -pe 's/foo/bar/g' ./*
(il perl uno fallirà per i nomi di file che terminano con | o spazio)).
-
File ricorsivi e regolari (compresi quelli nascosti ) in questa e in tutte le sottodirectory
find . -type f -exec sed -i 's/foo/bar/g' {} +Se stai usando zsh:
sed -i -- 's/foo/bar/g' **/*(D.)(potrebbe fallire se l'elenco è troppo grande, vedi
zargsper aggirare).Bash non può verificare direttamente la presenza di file regolari, è necessario un ciclo (le parentesi graffe evitano di impostare le opzioni a livello globale):
( shopt -s globstar dotglob; for file in **; do if [[ -f $file ]] && [[ -w $file ]]; then sed -i -- 's/foo/bar/g' "$file" fi done )I file vengono selezionati quando sono file effettivi (-f) e sono scrivibili (-w).
2. Sostituisci solo se il nome del file corrisponde a un'altra stringa / ha un'estensione specifica / è di un certo tipo ecc:
-
Non ricorsivo, solo file in questa directory:
sed -i -- 's/foo/bar/g' *baz* ## all files whose name contains baz sed -i -- 's/foo/bar/g' *.baz ## files ending in .baz -
File ricorsivi e regolari in questa e in tutte le sottodirectory
find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' {} +Se stai usando bash (le parentesi graffe evitano di impostare le opzioni a livello globale):
( shopt -s globstar dotglob sed -i -- 's/foo/bar/g' **baz* sed -i -- 's/foo/bar/g' **.baz )Se stai usando zsh:
sed -i -- 's/foo/bar/g' **/*baz*(D.) sed -i -- 's/foo/bar/g' **/*.baz(D.)
Il -- serve a dire a sed che non verranno più forniti flag nella riga di comando. Questo è utile per proteggere dai nomi di file che iniziano con - .
-
Se un file è di un certo tipo, ad esempio, eseguibile (vedi
man findper ulteriori opzioni):find . -type f -executable -exec sed -i 's/foo/bar/g' {} +
zsh :
sed -i -- 's/foo/bar/g' **/*(D*)
3. Sostituisci solo se la stringa si trova in un determinato contesto
-
Sostituisci
fooconbarsolo se esiste unbazpiù avanti sulla stessa riga:sed -i 's/foo(.*baz)/bar1/' file
In sed , utilizzando ( ) salva tutto ciò che è tra parentesi e puoi quindi accedervi con 1 . Esistono molte varianti di questo tema, per saperne di più su tali espressioni regolari, vedere qui.
-
Sostituisci
fooconbarsolo sefoosi trova nella colonna 3d (campo) del file di input (assumendo campi separati da spazi):gawk -i inplace '{gsub(/foo/,"baz",$3); print}' file
(richiede gawk 4.1.0 o successivo).
-
Per un campo diverso usa semplicemente
$NdoveNè il numero del campo di interesse. Per un separatore di campo diverso (:in questo esempio) usa:gawk -i inplace -F':' '{gsub(/foo/,"baz",$3);print}' file
Un'altra soluzione che utilizza perl :
perl -i -ane '$F[2]=~s/foo/baz/g; $" = " "; print "@Fn"' foo
NOTA:sia il awk e perl le soluzioni influiranno sulla spaziatura nel file (rimuovere gli spazi vuoti iniziali e finali e convertire le sequenze di spazi vuoti in uno spazio in quelle righe che corrispondono). Per un campo diverso, usa $F[N-1] dove N è il numero di campo desiderato e per un separatore di campo diverso utilizzare (il $"=":" imposta il separatore del campo di output su : ):
perl -i -F':' -ane '$F[2]=~s/foo/baz/g; $"=":";print "@F"' foo
-
Sostituisci
fooconbarsolo sulla 4a riga:sed -i '4s/foo/bar/g' file gawk -i inplace 'NR==4{gsub(/foo/,"baz")};1' file perl -i -pe 's/foo/bar/g if $.==4' file
4. Operazioni multiple di sostituzione:sostituisci con stringhe diverse
-
Puoi combinare
sedcomandi:sed -i 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file
Tieni presente che l'ordine è importante (sed 's/foo/bar/g; s/bar/baz/g' sostituirà foo con baz ).
-
o comandi Perl
perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file -
Se hai un numero elevato di pattern, è più facile salvare i tuoi pattern e le loro sostituzioni in un
sedfile di script:#! /usr/bin/sed -f s/foo/bar/g s/baz/zab/g -
Oppure, se hai troppe coppie di pattern perché quanto sopra sia fattibile, puoi leggere le coppie di pattern da un file (due pattern separati da spazi, $pattern e $replacement, per riga):
while read -r pattern replacement; do sed -i "s/$pattern/$replacement/" file done < patterns.txt -
Sarà piuttosto lento per lunghi elenchi di pattern e file di dati di grandi dimensioni, quindi potresti voler leggere i pattern e creare un
sedscript da loro invece. Quanto segue presuppone uno <spazio> delimitatore separa un elenco di MATCH<spazio>REPLACE coppie che si verificano una per riga nel filepatterns.txt:sed 's| *([^ ]*) *([^ ]*).*|s/1/2/g|' <patterns.txt | sed -f- ./editfile >outfile
Il formato sopra è in gran parte arbitrario e, ad esempio, non consente uno <spazio> in una delle PARTITA o SOSTITUIRE . Il metodo è però molto generale:in pratica, se riesci a creare un flusso di output che assomigli a un sed script, quindi puoi ottenere quel flusso come sed script specificando sed 's file di script come - stdin.
-
Puoi combinare e concatenare più script in modo simile:
SOME_PIPELINE | sed -e'#some expression script' -f./script_file -f- -e'#more inline expressions' ./actual_edit_file >./outfile
Un POSIX sed concatenerà tutti gli script in uno nell'ordine in cui appaiono sulla riga di comando. Nessuno di questi deve terminare con un n ewline.
-
greppuò funzionare allo stesso modo:sed -e'#generate a pattern list' <in | grep -f- ./grepped_file -
Quando si lavora con stringhe fisse come pattern, è buona norma evitare i metacaratteri dell'espressione regolare . Puoi farlo abbastanza facilmente:
sed 's/[]$&^*./[]/\&/g s| *([^ ]*) *([^ ]*).*|s/1/2/g| ' <patterns.txt | sed -f- ./editfile >outfile
5. Operazioni multiple di sostituzione:sostituisci più pattern con la stessa stringa
-
Sostituisci uno qualsiasi di
foo,barobazconfoobarsed -Ei 's/foo|bar|baz/foobar/g' file -
o
perl -i -pe 's/foo|bar|baz/foobar/g' file