Ricerca globale e sostituzione della riga di comando di Linux
Domanda
Sto cercando di cercare e sostituire una stringa in tutti i file corrispondenti a grep su una macchina Linux. Ho alcune parti di ciò che voglio fare, ma non sono sicuro del modo migliore per metterle insieme.
grep -n 'foo' *
mi darà l'output nel modulo:
[filename]:[line number]:[text]
Per ogni file restituito da grep, vorrei sostituire " foo " con " bar " e riscrivi il risultato nel file. C'è un buon modo per farlo? Forse una pipeline di fantasia?
Soluzione
Intendi cercare e sostituire una stringa in tutti i file corrispondenti a grep?
perl -p -i -e 's/oldstring/newstring/g' `grep -ril searchpattern *`
Modifica
Dal momento che questa sembra essere una domanda abbastanza popolare pensata che aggiornerei.
Oggi uso principalmente ack-grep
poiché è più intuitivo. Quindi il comando sopra sarebbe:
perl -p -i -e 's/old/new/g' `ack -l searchpattern`
Per gestire gli spazi bianchi nei nomi dei file è possibile eseguire:
ack --print0 -l searchpattern | xargs -0 perl -p -i -e 's/old/new/g'
puoi fare di più con <=>. Supponi di voler limitare la ricerca solo ai file HTML:
ack --print0 --html -l searchpattern | xargs -0 perl -p -i -e 's/old/new/g'
E se lo spazio bianco non è un problema è anche più breve:
perl -p -i -e 's/old/new/g' `ack -l --html searchpattern`
perl -p -i -e 's/old/new/g' `ack -f --html` # will match all html files
Altri suggerimenti
Questo sembra essere quello che vuoi, in base all'esempio che hai dato:
sed -i 's/foo/bar/g' *
Non è ricorsivo (non scenderà nelle sottodirectory). Per una buona soluzione che sostituisce i file selezionati in un albero, uso find:
find . -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;
Il * .html
è l'espressione che i file devono corrispondere, il .bak
dopo il -i
crea una copia del file originale , con un'estensione .bak (può essere qualsiasi estensione che ti piace) e il g
alla fine dell'espressione sed dice a sed di sostituire più copie su una riga (anziché solo la prima). Il -print
da trovare è una comodità per mostrare quali file sono stati abbinati. Tutto questo dipende dalle versioni esatte di questi strumenti sul tuo sistema.
Se il tuo sed (1)
ha un'opzione -i
, allora usalo in questo modo:
for i in *; do
sed -i 's/foo/bar/' $i
done
In caso contrario, ci sono diversi modi per variare in base alla lingua con cui vuoi giocare:
ruby -i.bak -pe 'sub(%r{foo}, 'bar')' *
perl -pi.bak -e 's/foo/bar/' *
Mi piace e ho usato la soluzione di cui sopra o una ricerca a livello di sistema e la sostituzione tra migliaia di file:
find -name '*.htm?' -print -exec sed -i.bak 's/foo/bar/g' {} \;
Presumo con '* .htm?' anziché .html cerca e trova file .htm e .html simili.
Sostituisco .bak con tilde (~) utilizzata più a livello di sistema per semplificare la pulizia dei file di backup.
Funziona usando grep senza bisogno di usare perl o find.
grep -rli 'old-word' * | xargs -i@ sed -i 's/old-word/new-word/g' @
trova. -type f -print0 | xargs -0 < sed / perl / ruby ??cmd >
elaborerà contemporaneamente più nomi di file contenuti nello spazio caricando un interprete per batch. Molto più veloce.
La risposta già fornita sull'uso di trova e sed
find -name '* .html' -print -exec sed -i.bak 's / foo / bar / g' {} \;
è probabilmente la risposta standard. Oppure potresti usare perl -pi -e s / foo / bar / g '
invece del comando sed
.
Per gli usi più rapidi, potresti trovare il comando rpl più facile da ricordare. Ecco la sostituzione (foo - > bar), ricorsivamente su tutti i file nella directory corrente:
rpl -R foo bar.
Non è disponibile per impostazione predefinita sulla maggior parte delle distribuzioni Linux ma è veloce da installare ( apt-get install rpl
o simile).
Tuttavia, per i lavori più difficili che comportano espressioni regolari e sostituzione sostitutiva, o rinominazioni di file e ricerca e sostituzione, lo strumento più generale e potente di cui sono a conoscenza è repren , un piccolo script Python che ho scritto qualche tempo fa per alcuni compiti di rinominazione e refactoring più spinosi. Le ragioni che potresti preferire sono:
- Supporta la ridenominazione dei file e la ricerca e sostituzione dei contenuti dei file (incluso lo spostamento dei file tra le directory e la creazione di nuove directory principali).
- Vedi le modifiche prima di impegnarti a eseguire la ricerca e sostituire.
- Supporta espressioni regolari con modalità di sostituzione di ritorno, parole intere, senza distinzione tra maiuscole e minuscole e preservazione delle maiuscole (sostituisci foo - > bar, Foo - > Bar, FOO - > BAR).
- Funziona con sostituzioni multiple, inclusi swap (foo - > bar and bar - > foo) o insiemi di sostituzioni non uniche (foo - > bar, f - > x).
Controlla il README per esempi.
Questo è in realtà più facile di quanto sembri.
grep -Rl 'foo' ./ | xargs -n 1 -I % sh -c "ls %; sed -i 's/foo/bar/g' %";
- grep ricorre attraverso il tuo albero (-R) e stampa solo il nome del file (-l), iniziando dalla directory corrente (./)
- che viene reindirizzato a xargs, che li elabora uno alla volta (-n 1) e usa% come segnaposto (-I%) in un comando shell (sh -c)
- nel comando shell, prima viene stampato il nome del file (ls%;)
- quindi sed esegue un'operazione in linea (-i), una sostituzione ('s /') di foo con bar (foo / bar), globalmente (/ g) sul file (di nuovo, rappresentato da%)
Facile peasy. Se hai una buona conoscenza di find, grep, xargs, sed e awk, quasi nulla è impossibile quando si tratta di manipolare i file di testo in bash :)