Come eseguire una sostituzione sul posto di sed che crea solo backup di file che sono stati modificati?


13

Ho eseguito quanto segue per sostituire un termine usato in tutti i file nella directory di lavoro corrente:

$ find . -type f -print0 | xargs -0 sed -i'.bup' -e's/Ms. Johnson/Mrs. Melbin/g'

Ciò ha eseguito la sostituzione di parola ma ha anche creato .bupfile di file che non hanno mai avuto il fileMs. Johnson stringa.

Come si esegue la sostituzione senza creare tutti questi backup non necessari?


mi sembra un insetto di sed see
Hotschke,

Probabilmente esiste un approccio migliore possibile utilizzando exe in modo condizionale (solo se il file viene modificato) in esecuzione :!cp '%' '%.bup'prima di salvare ed uscire. Potrebbe valere la pena darci un'occhiata.
Wildcard il

Ho scritto una domanda sulla mia idea sopra sullo viscambio di stack.
Wildcard il

Risposte:


6

È possibile controllare il contenuto dei file per assicurarsi che avvenga una sostituzione quando si sedopera su di essi:

find . \
    -type f \
    -exec grep -q 'Ms. Johnson' {} \; \
    -print0 |
xargs -0 sed -i'.bup' -e's/Ms. Johnson/Mrs. Melbin/g'

Se vuoi essere davvero intelligente, puoi rinunciare a usare find:

grep -Z -l -r 'Ms. Johnson' |
    xargs -0 sed -i'.bup' -e's/Ms. Johnson/Mrs. Melbin/g'

1
Penso che avete bisogno di un '{}'poco prima \;in quella -exec.
Jander,

8

Trova ha un -execoperatore che esegue un comando arbitrario. Ancora meglio, -execè un test , quindi è possibile concatenare più -execs insieme, e se i comandi precedenti falliscono, quelli successivi non verranno eseguiti. La stringa {}viene sostituita con il nome file corrente e; segna la fine del comando. Dovresti citare entrambi per evitare che la shell interferisca.

Così:

find . -type f \
    -exec grep -q 'Ms. Johnson' '{}' \; \
    -exec sed -i'.bup' -e's/Ms. Johnson/Mrs. Melbin/g' '{}' \;

Non ho mai avuto problemi con l'utilizzo di bare {}.
anfetamachina,

1
@amphetamachine: Nemmeno io, ma la pagina man lo suggerisce. Potrebbe essere una cosa csh, oppure potrebbero esserci shell (non Bash e non POSIX) che si espandono {}nella stringa null quando si fa l'espansione del controvento.
Jander,

4

Ho guardato la pagina man e non ho visto alcun modo per farlo direttamente sed, come sono sicuro che hai fatto prima di chiedere. Vedo diversi modi per aggirare questo problema usando grep, ma penso che il più semplice sia questo:

grep -rlZ "Ms. Johnson" . | xargs -0 sed -i'.bup' -e's/Ms. Johnson/Mrs. Melbin/g'

-rrecurse
-lprint il nome file
-Ztermina solo con null


-1

Sembra che tu debba creare un elenco di file che corrispondono prima ai termini di ricerca.

search="test"
for match in $(find -type f -exec grep -l $search "{}" \;)
do sed -i'.bup' -e "/$search/ s/$search/Mrs. Melbin/g" "$match"
done

Hai dimenticato il .primo argomento find. Inoltre, come mi è stato comunicato non molto tempo fa, in realtà non è necessario citare o scappare{}
Kevin,

Non generare un elenco di file ed eseguire la sostituzione dei comandi su quello. Prima findgenera un elenco di file separato da una nuova riga, quindi la shell rompe questo elenco in qualsiasi spazio bianco (non solo nuove righe) e quindi la shell tratta ogni parola come un modello glob. Funziona solo se i nomi dei tuoi file non contengono spazi bianchi o \[?*. Altre risposte su questa pagina mostrano come farlo correttamente.
Gilles 'SO- smetti di essere malvagio' il

@Kevin Il .non è necessario con find (almeno find found in linux) perché si presume che quando non viene passato alcun percorso a un argomento.
laebshade,
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.